详细说明yurib 的回答,我会做这样的事情(也发布在 igraph 邮件列表上):
我将使用两个属性,一个用于顶点,一个用于边缘。edge 属性很简单,它会被调用cut_value
,它要么是要么None
包含您要查找的值。最初,所有这些值都是None
s。我们将调用cut_value=None
未处理的边和 ``cut_value 不是 None```处理的边。
vertex 属性将被调用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。如果deg[u]
变为 1,我们将u放入队列中。我们还将其cut_size
加一,因为v现在是图的一部分,当我们稍后移除u上的最后一个边缘事件时,该部分将被分离。
在 Python 中,这应该类似于以下代码(未经测试):
weights = graph.es["weight"]
while q:
# Step 1
v = q.popleft()
neis = [e for e in graph.incident(v) if cut_value[e] is None]
if len(neis) != 1:
raise ValueError("this should not have happened")
e = neis[0]
# Step 2
cut_values[e] = weights[e] * min(cut_sizes[v], n - cut_sizes[v])
# Step 3
endpoints = graph.es[e].tuple
u = endpoints[1] if endpoints[0] == v else endpoints[0]
deg[u] -= 1
if deg[u] == 1:
q.append(u)
cut_sizes[u] += 1