【问题标题】:Why computing Preferential Attachment is costly?为什么计算 Preferential Attachment 成本高?
【发布时间】:2014-04-28 12:28:19
【问题描述】:

我有一个有 1034 个顶点和 53498 个边的无向图。我正在计算顶点的优先附件索引。两个顶点之间的优先连接相似度定义为第一个顶点的度数乘以第二个顶点的度数的乘积。我注意到我的计算速度很慢。计算上述图表需要 2.7 分钟。我不确定是我的算法速度慢还是其他问题。如果有人能看一下我的代码,我将非常感激。

编辑:我刚刚意识到 S 是一个 1034_by_1034 矩阵。查看嵌套的 for 循环,它似乎是一个 O(n^2) 算法!我想这就是为什么它很慢。你不同意吗?

def pa(graph):
    """
        Calculates Preferential Attachment index.

        Returns S the similarity matrix.
    """
    A = gts.adjacency(graph)
    S = np.zeros(A.shape)
    for i in xrange(S.shape[0]):
        for j in xrange(S.shape[0]):
            i_degree = graph.vertex(i).out_degree()
            j_degree = graph.vertex(j).out_degree()
            factor = i_degree * j_degree
            S[i,j] = factor
    return S

【问题讨论】:

  • 我不懂python。也许您可以将所有 out_degree(x) 保存在一个数组中。然后做乘法。
  • 我认为这取决于out_degree()函数是如何实现的。如果是O(1),我相信它会很快完成。
  • 一般来说,python 中的 for 循环不适合数字内容。如果您可以将这些 for 循环下推到 numpy 级别(或使用 numba/cython),您可以期待大幅加速。在 numpy 中,大多数事情都可以在没有 for 循环的情况下完成,通过在某些方法上使用轴参数,或者只是巧妙地使用索引。如果你展示那个 graph_vertex(i).out_degree() 方法,我们可能会加快很多
  • 确实是O(N^2)操作。但是 100 万个元素对于计算机来说仍然很小,它应该在一秒钟内完成,考虑到它只是你的代码需要的乘法。绝对out_degree() 部分需要很长时间。可以尝试将i_degreej_degree部分换成常量看看效果。
  • 我认为说“由于这个算法是O(N^2),它很慢”是一个逻辑谬误,因为它取决于输入大小。在这种情况下,对于O(N^2) 算法,只有数千的输入大小仍然在一秒钟内运行。在你的情况下,我相信它在其他部分很慢。您是否仅使用以下语句运行该函数? A = gts.adjacency(graph)

标签: python performance algorithm numpy graph


【解决方案1】:

据我所知,以下是我可以建议的加速:

第零加速:i_degree 不依赖于 j,因此将其上移一级

def pa(graph):
    A = gts.adjacency(graph)
    S = np.zeros(A.shape)
    for i in xrange(S.shape[0]):
        i_degree = graph.vertex(i).out_degree() # easy to see that this can be put here instead, since it does not depend on j 
        for j in xrange(S.shape[0]):
            j_degree = graph.vertex(j).out_degree()
            factor = i_degree * j_degree
            S[i,j] = factor
    return S

第一次加速:只调用 out_degree() N 次,而不是 2N^2。

def pa2(graph):
    A = gts.adjacency(graph)
    i_degree = numpy.zeros(A.shape[0])
    for i in xrange(A.shape[0]):
        i_degree[i] = graph.vertex(i).out_degree()
    S = np.zeros(A.shape)
    for i in xrange(S.shape[0]):
        for j in xrange(S.shape[0]):
            S[i,j] = i_degree[i]*i_degree[j]
    return S

第二个加速:numpy 代替 python for-loop

def pa3(graph):
    A = gts.adjacency(graph)
    i_degree = numpy.zeros(A.shape[0])
    for i in xrange(A.shape[0]):
        i_degree[i] = graph.vertex(i).out_degree()
    S = i_degree[:,None]*i_degree[None,:]
    return S

这滥用了您问题的对称性。

注意:[None,:] 与使用 [numpy.newaxis,:] 的作用相同。如果你想保留你的代码,你也可以在 out_degree() 方法上使用 @memoize 装饰器,但最好只在递归的东西上使用它,这不是其中一种情况。

【讨论】:

  • +1 我相信这会更快。 (我不知道你可以在 Numpy 中做这样的交叉产品)
  • @justhalf 我对加速进行了更多解释。最快的加速将调用 out_degree() 的 N 次,而不是 N^2 次。将乘法下推到 numpy 级别也应该加快一点,但是要知道哪个加速最大取决于 out_degree 方法的实现。 N^2 乘法只是在 numpy (读取:C)中完成,因此开销比普通 python 少。调用 out_degree() 的 N^2 次已改进为 N 次。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-24
  • 1970-01-01
  • 2023-03-28
  • 1970-01-01
  • 2021-11-16
  • 2015-02-01
相关资源
最近更新 更多