我想在顶点超过1.5M的图形中计算聚类系数。
我有一个Dictionary
顶点ID作为键,值为List
,其中所有顶点都连接到顶点ID。
聚类系数= 3 *三角形/连接的三元组数。
问题是:计算图表中的三角形数量需要4个多小时。
我的代码:
List<string> list_t = new List<string>();
Dictionary<string, List<string>> copy = new Dictionary<string, List<string>>(Global.dict_edge_undirected);
int triangles = 0; // (number of triangles in graph)*3
foreach (KeyValuePair<string, List<string>> pair in copy)
{
if (pair.Value.Count > 1)
{
foreach (string neigh1 in pair.Value)
{
list_t = copy[neigh1];
foreach (string neigh2 in pair.Value)
{
if (neigh1 != neigh2 && list_t.Contains(neigh2))
{
triangles++;
}
}
}
}
}
如何减少运行时间?
C ++ igraph库在不到3分钟的时间内计算出该图的聚类系数。
我错过了什么?
答案 0 :(得分:5)
您至少可以使用HashSet<T>
代替列表:
var copy = new Dictionary<string, HashSet<string>>(Global.dict_edge_undirected);
int triangles = 0; // (number of triangles in graph)*3
foreach (var pair in copy)
{
if (pair.Value.Count > 1)
{
foreach (string neigh1 in pair.Value)
{
triangles += pair.Value.Count(neigh2 => neigh1 != neigh2 && copy[neigh1].Contains(neigh2));
}
}
}
它删除了一个内部循环,因为Contains
方法对于散列表是O(1)。
秒 - 您可以使用int
ID代替字符串(并使用Dictionary<int,string>
或简单地使用字符串[]来按ID获取字符串)。它将删除具有相同哈希码的字符串的额外比较。
在这种情况下,您不需要使用字典,只需使用数组:
const int N = 100;
var copy = new HashSet<int>[N];
int triangles = 0; // (number of triangles in graph)*3
foreach (var pair in copy)
{
if (pair.Count > 1)
{
foreach (int neigh1 in pair)
{
triangles += pair.Count(neigh2 => neigh1 != neigh2 && copy[neigh1].Contains(neigh2));
}
}
}
所有带数组的操作都由JIT优化。
最后,您可以使用Parallel
来提升效果(本地变量用于最小化Interlocked.Add
次调用)
int totalTriangles = 0;
Parallel.ForEach(copy, () => 0,
(set, _, __) =>
{
int triangles = 0;
if (set.Count > 1)
{
foreach (int neigh1 in set)
{
triangles += set.Count(neigh2 => neigh1 != neigh2 &&
copy[neigh1].Contains(neigh2));
}
}
return triangles;
},
i => Interlocked.Add(ref totalTriangles, i));
或使用PLINQ:
const int N = 100;
var copy = new HashSet<int>[N];
int triangles = copy.AsParallel().Where(pair => pair.Count > 1).Sum(pair => pair.Sum(neigh1 => pair.Count(neigh2 => neigh1 != neigh2 && copy[neigh1].Contains(neigh2))));