我正在处理一些带有 no 周期的(有多个)有向图,我需要找到任意两个节点之间的所有简单路径。一般来说,我不担心执行时间,但我必须在非常多的时间步骤中为很多节点执行此操作 - 我正在处理基于时间的模拟。
我曾经尝试过NetworkX提供的设施,但总的来说我发现它们比我的方法慢。不确定最近有什么变化。
我已经实现了这个递归函数:
import timeit
def all_simple_paths(adjlist, start, end, path):
path = path + [start]
if start == end:
return [path]
paths = []
for child in adjlist[start]:
if child not in path:
child_paths = all_simple_paths(adjlist, child, end, path)
paths.extend(child_paths)
return paths
fid = open('digraph.txt', 'rt')
adjlist = eval(fid.read().strip())
number = 1000
stmnt = 'all_simple_paths(adjlist, 166, 180, [])'
setup = 'from __main__ import all_simple_paths, adjlist'
elapsed = timeit.timeit(stmnt, setup=setup, number=number)/number
print 'Elapsed: %0.2f ms'%(1000*elapsed)
在我的计算机上,每次迭代平均得到1.5毫秒。我知道这是一个很小的数字,但我必须多次执行非常的操作。
如果您感兴趣,我已经上传了一个包含邻接列表的小文件:
我使用邻接列表作为输入,来自NetworkX DiGraph表示。
任何改进算法的建议(即,是否必须递归?)或我可能会尝试的其他方法都非常受欢迎。
谢谢。
安德烈。
答案 0 :(得分:1)
您可以通过缓存共享子问题的结果来节省时间而无需更改算法逻辑。
例如,在下图中调用all_simple_paths(adjlist, 'A', 'D', [])
会多次计算all_simple_paths(adjlist, 'D', 'E', [])
:
Python为此任务提供了内置装饰器lru_cache
。它使用哈希来记忆参数,因此您需要将adjList
和path
更改为tuple
,因为list
不可用。
import timeit
import functools
@functools.lru_cache()
def all_simple_paths(adjlist, start, end, path):
path = path + (start,)
if start == end:
return [path]
paths = []
for child in adjlist[start]:
if child not in path:
child_paths = all_simple_paths(tuple(adjlist), child, end, path)
paths.extend(child_paths)
return paths
fid = open('digraph.txt', 'rt')
adjlist = eval(fid.read().strip())
# you can also change your data format in txt
adjlist = tuple(tuple(pair)for pair in adjlist)
number = 1000
stmnt = 'all_simple_paths(adjlist, 166, 180, ())'
setup = 'from __main__ import all_simple_paths, adjlist'
elapsed = timeit.timeit(stmnt, setup=setup, number=number)/number
print('Elapsed: %0.2f ms'%(1000*elapsed))
我机器上的运行时间:
- 原件:0.86ms
- 缓存:0.01ms
这种方法只有在有很多共享子问题时才能运行。