聚类系数C#

时间:2014-12-02 13:28:02

标签: c#

我想在顶点超过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分钟的时间内计算出该图的聚类系数。

我错过了什么?

1 个答案:

答案 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))));