source_node.children
产生一个可迭代对象,该对象将遍历source_node
的所有子节点。它还依赖于有一种有效的方法,用于比较两个节点以查看它们是否相同,并且使用is
可能更好。显然,在您使用的库中,获取包含所有子项的可迭代对象的语法是graph[source_node]
,因此您需要相应地调整代码。def allpaths(source_node, sink_node):
if source_node == sink_node: # Handle trivial case
return frozenset([(source_node,)])
else:
result = set()
for new_source in source_node.children:
paths = allpaths(new_source, sink_node, memo_dict)
for path in paths:
path = (source_node,) + path
result.add(path)
result = frozenset(result)
return result
def allpaths(source_node, sink_node, memo_dict = None):
if memo_dict is None:
# putting {}, or any other mutable object
# as the default argument is wrong
memo_dict = dict()
if source_node == sink_node: # Don't memoize trivial case
return frozenset([(source_node,)])
else:
pair = (source_node, sink_node)
if pair in memo_dict: # Is answer memoized already?
return memo_dict[pair]
else:
result = set()
for new_source in source_node.children:
paths = allpaths(new_source, sink_node, memo_dict)
for path in paths:
path = (source_node,) + path
result.add(path)
result = frozenset(result)
# Memoize answer
memo_dict[(source_node, sink_node)] = result
return result
这还可以让您在调用之间保存记忆化字典,因此,如果您需要计算多个源和汇节点的答案,则可以避免大量额外的工作。
path = (source_node,) + path
时出现了以下错误:TypeError: can only concatenate tuple (not "instance") to tuple
。 - Tylerallpaths
的例子中使用了可变默认参数,如果在同一个程序中调用该函数多次(递归不计算),会导致混乱。已经修复。 - Boris Gorelikdef find_all_paths(graph, start, end):
path = []
paths = []
queue = [(start, end, path)]
while queue:
start, end, path = queue.pop()
print 'PATH', path
path = path + [start]
if start == end:
paths.append(path)
for node in set(graph[start]).difference(path):
queue.append((node, end, path))
return paths
我不确定是否有特殊的优化可用 - 在寻找它们之前,我会做一个简单的递归解决方案,例如(仅使用networkx中将图通过节点索引的功能给出其邻居节点的迭代器[[在networkx的情况下为字典,但我没有特别利用它]]) ...:
def allpaths(G, source_nodes, set_of_sink_nodes, path_prefix=()):
set_of_result_paths = set()
for n in source_nodes:
next_from_n = []
for an in G[n]:
if an in set_of_sink_nodes:
set_of_result_paths.add(path_prefix + (n, an))
else:
next_from_n.append(an)
if next_from_n:
set_of_result_paths.update(
allpaths(G, next_from_n, set_of_sink_nodes, path_prefix + (n,)))
return set_of_result_paths
这应该是可以证明正确的(但我不会去证明,因为现在已经很晚了,而且我感到很累和头脑混乱;-) 并且可用于验证任何进一步的优化;-)。
我首先尝试的优化是某种简单的记忆化:如果我已经计算出从某个节点N到任何目标节点(无论我在进行该计算时前缀是什么)的路径集合,我可以将其存储在字典中的键N下,并避免在以后通过不同的路线再次到达N时进行进一步的重新计算;-)。