有效地创建和存储所有有效的"来自一组边

时间:2018-01-16 23:39:07

标签: python algorithm graph combinatorics traveling-salesman

我认为这是一个相当困难的问题。所以,提前感谢您的帮助。

我有一个我正在遍历的图表,一次创建一个不同的路径。 我有一套边缘我必须"使用,每个都存储为(to,from)的元组。我避免重复节点,所以我们只去每个"到#34;一次,每次"来自"一旦。我也不想在组合中创建一个循环。

我想创建边的所有组合(顺序很重要)。具体来说,我想要所有有效大小的元组的所有组合。

清晰的一些例子:

Valid combinations of edges:

((5,7))
((3,9),(9,11),(21,18))
((1,2),(2,3),(3,4),(4,5))

Invalid combinations:
((9,8),(9,6))
((2,5),(11,3),(8,5))
((1,2),(2,3),(3,4),(4,1))

所以,我们可以看到的一件事是,要制作所有组合,我们将制作大小为1的组合,然后是大小为2,然后为3,... 4 .... n

不用担心,这并不是很疯狂。我创建组合的边缘数量通常不是那么多。但它是可变的,谁知道,也许我最终可能会创建一些大小为n的组合。

所以,我正在考虑使用itertools来生成组合。我可以在循环中放置itertools组合,并增加每次传递组合的大小。

但后来我才意识到,大多数组合实际上可能最终无效,如果我使用itertools,我不认为我可以检查它们的有效性,直到生成整个组合。这看起来非常低效。

所以,我的思考就是使用邻接列表,其中我要强制的任何边(to,from)存储在indices [to] [from]中。这允许我迭代邻接列表,以避免重复"或#34;来自"来自"。

但是,我仍然无法概念化我如何实际编写一个函数,通过遍历邻接列表生成我想要的所有组合。

任何想法?

注意:就目前而言,如果有人选择忽略避免封闭周期的挑战,我不介意,即:1,2,3,4,1

1 个答案:

答案 0 :(得分:2)

以下是递归生成器函数。它将边缘列表预处理成邻接字典。它将生成长度为l的所有路径:

from collections import defaultdict

# preprocess for better performance
edges = [(1,2),(2,1),(2,3),(3,4),(4,5),(5,7), (3,9),(9,11),(11,18)]
graph = defaultdict(set)
for f, t in edges:
    graph[f].add(t)

def paths(graph, l, fr=None, avoid=None):
    avoid = avoid or set()  # avoid previous nodes
    if l == 0:  # base case: empty path
        yield ()
    from_nodes = [fr] if fr else graph.keys()
    for f in from_nodes:
        new_avoid = avoid | set([f])  # set union
        for t in graph[f]:  # for every adjacent node t...
            if t not in avoid:  # unless it has been seen
                # take all paths starting at t with length l-1
                for path in paths(graph, l-1, fr=t, avoid=new_avoid):
                    # and prepend the current edge
                    yield ((f, t),) + path 

>>> list(paths(graph, 2))
[((1, 2), (2, 3)),
 ((2, 3), (3, 9)),  # no cycle: ((1, 2), (2, 1))
 ((2, 3), (3, 4)),
 ((3, 9), (9, 11)),
 ((3, 4), (4, 5)),
 ((4, 5), (5, 7)),
 ((9, 11), (11, 18))]

>>> list(paths(graph, 3))
[((1, 2), (2, 3), (3, 9)),
 ((1, 2), (2, 3), (3, 4)),
 ((2, 3), (3, 9), (9, 11)),
 ((2, 3), (3, 4), (4, 5)),
 ((3, 9), (9, 11), (11, 18)),
 ((3, 4), (4, 5), (5, 7))]