Python:有向图中的所有简单路径

时间:2017-09-30 06:48:57

标签: python networkx

我正在处理一些带有 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毫秒。我知道这是一个很小的数字,但我必须多次执行非常的操作。

如果您感兴趣,我已经上传了一个包含邻接列表的小文件:

adjlist

我使用邻接列表作为输入,来自NetworkX DiGraph表示。

任何改进算法的建议(即,是否必须递归?)或我可能会尝试的其他方法都非常受欢迎。

谢谢。

安德烈。

1 个答案:

答案 0 :(得分:1)

您可以通过缓存共享子问题的结果来节省时间而无需更改算法逻辑。

例如,在下图中调用all_simple_paths(adjlist, 'A', 'D', [])会多次计算all_simple_paths(adjlist, 'D', 'E', [])enter image description here

Python为此任务提供了内置装饰器lru_cache。它使用哈希来记忆参数,因此您需要将adjListpath更改为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

这种方法只有在有很多共享子问题时才能运行。