在igraph或networkx中,如何在稀疏有向图中找到所有长度为4的简单路径是最快的?一种方法是创建一个长度为4的简单路径图,并使用子图同构vf2函数。这是最好/最快的方法吗?
我没有源节点,希望找到整个图中存在的所有长度为4的简单路径。
在我的数据中,可能很少有这样的路径,我希望能够高效地迭代它们。
我没有源节点,希望找到整个图中存在的所有长度为4的简单路径。
在我的数据中,可能很少有这样的路径,我希望能够高效地迭代它们。
Using a function like this:
def simple_paths(start, length, visited=[]):
if length==0:
yield(visited + [start])
else:
for child in children(start):
if child not in visited:
for path in simple_paths(child, length-1, visited + [start]):
yield(path)
for start in nodes():
for path in simple_paths(start, 4):
print path
nodes()
返回图中所有节点的可迭代对象,而children(x)
返回节点x
的子节点的可迭代对象。
simple_paths()
函数应用于上述图形正确地产生:
['5', '9', '3', '1', '0']
['6', '5', '9', '3', '1']
['6', '5', '3', '1', '0']
['9', '5', '3', '1', '0']
['6', '5', '1', '3', '9']
)['6', '5', '3', '1', '5']
)children(start)
替换为nx.neighbors(G, start)
,将nodes()
替换为nx.nodes(G)
。我认为将children()
重命名为neighbors()
会更有意义,因为图不一定是树形结构。 - Luqmaan首先,让我们解决一个更简单的问题-计算长度为4的路径数。
1)设A是给定图的邻接矩阵。A [i] [j] = 1
表示存在I和J之间的边,否则为0。 A ^ N
给出了某个固定长度N
的路径的数量。
2)矩阵平方的形式如下:
init(RES,0);
for(row=1;N)
for(col=1;N)
for(common=1;N)
RES[row][col] + = a[row][common]*a[common][col];
A
的每个度数deg
,A[i][j]
存储从i
到j
长度为deg
的路径数。在第一阶段邻接矩阵只存储长度为1的路径数量。当你将A^N乘以A
时,你正在尝试将长度为N
的路径扩展到N+1
。
a[row][common]*a[common][col]
可以解释为“从row
到common
有a[row][common]
种长度为1的方式,而从common
到col
有a[common][col]
种长度为1的方式。根据组合数学的乘法原理,从行到列的长度为1的方法数是a[row][common]*a[common][col]
”。
vector
或ArrayList
。将RES[row][col]+ = a[row][common]*a[common][col]
替换为RES[row][col].push_back(cartesian_product(a[row][common],a[common][col]))
。仅计算路径的复杂度为matrix multiplication*degree
= N^3*degree。应用二进制指数运算,您可以获得N^3*log(degree)。在我们的情况下,degree=4,log(4)=2,2~4-无关紧要。但现在您不能只乘2个数字,而应该对长度为N的向量进行笛卡尔积 - 路径。因此,在常见情况下,复杂度增加到N,但在我们的情况下增加到4)。n1 = adjacency_lists()
n2 = [[(a, b) for a in x for b in n1[a] if i != b] for i, x in enumerate(n1)]
n4 = [[(a, b, c, d) for (a, b) in x for (c, d) in n2[b]
if i != c and i != d and a != c and a != d]
for (i, x) in enumerate(n2)]
if
子句确保路径是简单的。我假设没有从一个节点到自身的边。如果不是这样,请在n2
列表解析中添加额外的if i != a
子句(在for b in n1[a]
之前),并将a != b
添加到现有的if
子句中。for i, paths in enumerate(n4):
for a, b, c, d in paths:
print("{}->{}->{}->{}->{}".format(i, a, b, c, d))
O(N*D^4)
,其中N
是节点数,D
是节点的最大度数(这是我认为最好的方法)。我怀疑在纯Python中没有更快的解决方案。使用在C中实现的图形库调用的解决方案可能会快几个(可能很大)常数因子。您可以在这里找到相关文档 :)
本质上,它只是某种树搜索
您可以使用节点列表并执行此操作
list_of_nodes = nodes(my_graph)
for mysource in list_of_nodes:
for mytarget in list_of_nodes:
if source != target:
print all_simple_paths(my_graph,source=mysource,target=mytarget, cutoff=4)
paths = all_pairs_shortest_path(my_graph,cutoff=4)
for k,v in paths:
if len(paths[k][v]) == 4:
print paths[k][v]
没有真正的命令可以生成特定长度的所有简单路径,因为找到这个路径的唯一方法是从每个节点生成一棵树,例如执行上面修改过的for循环的修改版本,因为它使用了修改过的深度优先搜索。
如果您需要论文引用。检查简单路径的唯一方法是从每个节点进行检查。如果您有一个包含90000个元素的图形;没有其他方法检查它们是否连接 :/。目标并不重要,因为它只是另一个截止点,但如果您有大量的节点,则可能会有所影响:)。
" def _all_simple_paths_graph(G, source, target, cutoff=None):
if cutoff < 1:
return
visited = [source]
stack = [iter(G[source])]
while stack:
children = stack[-1]
child = next(children, None)
if child is None:
stack.pop()
visited.pop()
elif len(visited) < cutoff:
if child == target:
yield visited + [target]
elif child not in visited:
visited.append(child)
stack.append(iter(G[child]))
else: #len(visited) == cutoff:
if child == target or target in children:
yield visited + [target]
stack.pop()
visited.pop()"
上面的代码来自networkx文档
要修改它以生成没有“目标”截止的DFS,您可以使用:
def _all_simple_paths_graph(G, source, target, cutoff=None):
if cutoff < 1:
return
visited = [source]
stack = [iter(G[source])]
while stack:
children = stack[-1]
child = next(children, None)
if child is None:
stack.pop()
visited.pop()
elif len(visited) < cutoff:
#if child == target:
# yield visited + [target]
#elif child not in visited:
if child not in visited:
visited.append(child)
stack.append(iter(G[child]))
#else: #len(visited) == cutoff:
#if child == target or target in children:
# yield visited + [target]
#stack.pop()
#visited.pop()