我使用igraph(通过python)进行图聚类。
我有一棵树(几何图形的最小生成树),带有加权边,并且想要计算重量时间 如果边是两个组件的顶点数较少 删除:
def sep(graph, e):
h = copy(graph)
w = h.es[e]['weight']
h.delete_edges(e)
return w * min(h.components().sizes())
# 'graph' is the tree I am dealing with
ss = [sep(graph,x) for x in range(len(graph.es))]
我的问题是:
这是图论中的已知(和命名)属性吗?如果是这样,那是什么 它
如果我为所有人计算这段代码,我的代码效率非常低 边缘,如上所示。如果图形变为50000个边和顶点, 内存消耗变得巨大。你有什么建议吗? 优化
答案 0 :(得分:1)
我不知道你的第一个问题,但我可能对第二个问题有所了解
由于我们正在处理最小的生成树,您可以使用关于一条边获得的信息来计算与其相邻的边的所需属性(从现在开始,我将此属性称为f(e)边缘e)。
让我们看一下边缘(A,B)和(B,C)。在计算f(A,B)
时,我们假设在从图表中删除边缘后,您发现较小的组件是A侧的组件,您知道:
f(B,C) = (f(A,B) / weight(A,B) + 1) * weight(B,C)
这是正确的,因为(B,C)与(A,B)相邻,并且在移除它之后,您将得到“几乎”相同的两个分量,唯一的区别是B从较大的分量移动到较小的分量。
这样,您可以对一个边进行完整的计算(包括删除dge和发现组件及其大小),然后只对连接到它的每个其他边进行简短计算。
您将需要特别注意这种情况,即较小的组件(随着我们沿着边缘链生长)会变得更大。
更新:
经过一番思考后,我意识到如果你能在树中找到一个叶子节点,那么你根本不需要搜索组件及其大小。首先计算附加到此节点的边的f(e)。因为它的叶子:
f(e) = weight(e) * 1
(1因为它是一个叶子节点,在删除边缘后,你会得到一个只包含叶子的组件和一个组件,它是图表的其余部分。)
从这里继续,如前所述......
排除查找叶节点所需的资源和时间,其余的计算将在O(m)(m是边数)和使用常量内存中完成。
答案 1 :(得分:1)
在yurib's answer上详细说明,我会做这样的事情(也发布在igraph邮件列表上):
我将使用两个属性,一个用于顶点,一个用于边缘。 edge属性很简单,它将被称为cut_value
,它可以是None
或包含您要查找的值。最初,所有这些值都是None
s。我们将使用cut_value=None
未处理的和边缘调用边缘,其中``cut_value不是None``` 已处理。
顶点属性将被称为cut_size
,它仅对存在一个未经处理的事件边缘的顶点有效。由于您有一棵树,除非处理完所有边(您可以终止算法),否则您将始终至少有一个这样的顶点。最初,对于所有顶点,cut_size
将为1(但请记住,它们仅对具有一个未处理的事件边缘的顶点有效)。
我们还将有一个列表deg
,其中包含在给定节点上发生事件的未处理边数。最初所有边都是未处理的,因此该列表包含顶点的度数。
到目前为止,我们有这个:
n, m = graph.vcount(), graph.ecount()
cut_values = [None] * m
cut_sizes = [1] * n
deg = graph.degree()
我们将始终使用一个事件未处理的边缘处理顶点。最初,我们将这些放入队列中:
from collections import deque
q = deque(v for v, d in enumerate(deg) if d == 1)
然后我们按如下方式逐个处理队列中的顶点,直到队列变空:
首先,我们从队列中删除顶点 v 并找到其唯一未处理的事件边缘。让这条边用 e
e 的cut_value
是 e 的权重乘以min(cut_size[v], n - cut_size[v])
。
让 e 的另一个端点用 u 表示。由于 e 现在已经处理,因此 u 上的未处理边数减少了1,因此我们必须将deg[u]
减少1.如果{{1} }变为1,我们将 u 放入队列中。我们还将其deg[u]
增加一个,因为 v 现在是图表中的一部分,当我们稍后移除 u 上的最后一个边缘事件时,它们将被分开
在Python中,这应该类似于以下代码(未经测试):
cut_size