【问题标题】:Finding combinations of pairs (connections)寻找对的组合(连接)
【发布时间】:2017-01-17 08:02:59
【问题描述】:

我特别对 Python 感兴趣,但也非常感谢通用解决方案。我有偶数个节点(假设是 12 个特定示例):

['a1', 'a2', 'a3', 'b1', 'b2', 'b3', 'c1', 'c2', 'c3', 'd1', 'd2', 'd3 ']

每个节点必须连接到另一个节点,形成 6 个连接(对)。 我需要找到一种方法来找到所有可能的连接组合。 此外,'a1'-'a2' 应视为与'a2'-'a1' 相同

一些想法:

  • 我可以获得可能的itertools.combinations(lst, 2) 列表,但节点不可重用。比如说,连接'a1'<->'a2' 应该从可用选项中消除'a1'<->'a3',因为'a1' 已经使用。

  • 我不知道搜索是否适用,因为似乎存在一些问题:

    • 似乎没有(简单、便宜)的方法来跟踪访问过的状态
    • 解决方案总是在底部(需要遍历所有树 向下完成所有连接)

【问题讨论】:

  • 您想找到所有可能的连接配置,对吗?也就是说,列表 [ [1,2], [3,4], ... [11,12] ] 代表一种配置,您希望找到所有可能的此类列表。
  • 这是有向图还是无向图?例如,a1-a2 是否与 a2-a1 相同?
  • 是的,这就是我要找的,a1-a2 和 a2-a1 一样

标签: python combinations itertools


【解决方案1】:

这是您问题的递归解决方案:

def findPairs(l):
    # recursion anchor, basic case:
    # when we have 2 nodes only one pair is possible
    if len(l) == 2:
        yield [tuple(l)]
    else:
        # Pair the first node with all the others
        for i in range(1, len(l)):
            # Current pair
            pair1 = [(l[0], l[i])]
            # Combine it with all pairs among the remaining nodes
            remaining = l[1:i] + l[i+1:]
            for otherPairs in findPairs(remaining):
                yield pair1 + otherPairs

可以据此计算所有解的个数:

def N(n):
    if n == 2:
        return 1
    return (n-1) * N(n-2)

请注意,我没有检查n % 2 == 0

同样对于n==len(l)==12,您将获得 10395 组可能的组合,这是非常可行的。但是这段代码虽然简短易读,但会一遍又一遍地生成和重新生成列表和元组,这使得它变得很慢。
看看它对你来说是否足够快,否则我们将不得不对其进行调整。

【讨论】:

  • 这很奇怪,我正在尝试测试它,如果我打印 list(findPairs(l)),它可以正常工作,但如果我打印 findPairs(l).next() 它只给出第一个配置:-(
  • 那是因为它返回一个generatorlist(findPairs(l)) 可能是您需要的,除非您尝试查找包含 18 个节点的列表的所有组合,这将花费很长时间。这就是生成器的用途。
  • 是的,我知道这是一台发电机。所以 next() 函数应该从生成器中获取下一个项目。但它没有,它会不断返回第一个组合,就好像有什么东西重置了它一样。
  • 无论如何,我都在解决它。天哪,这确实是10395个组合!你是个魔术师。而且我了解到在这种情况下我的任务没有解决方案:-)
  • 我想你正在做类似next(findPairs(l)) 的事情。这样,您将始终在新创建的生成器上调用next,该生成器确实只获取第一个元素。您需要将生成器保存到一个变量中(例如pairGen),然后反复调用next(pairGen);)
【解决方案2】:

我认为你只需要使用排列和take adjacent pairs,然后 删除任何倒序重复:

nodes = 'A1 A2 B1 B2 C1 C2'.split()
perms = set()
for perm in itertools.permutations(nodes):
    p = tuple(sorted(
        [tuple(sorted(perm[i:i + 2])) for i in range(0, len(perm), 2)]))
    perms.add(p)

for perm in sorted(perms):
    print(perm)

那么这是如何工作的呢?

我们需要遍历节点的每个排列:

for perm in itertools.permutations(nodes):

然后从排列中的相邻节点创建对:

[tuple(sorted(perm[i:i + 2])) for i in range(0, len(perm), 2)]

这是通过获取两个相邻节点并对它们进行排序来完成的,因此我们 以后可以寻找骗子:

tuple(sorted(perm[i:i + 2]))

然后创建一个tuple 以便我们有一些immutable 允许 索引。然后将所有对进行一个排列并执行相同的操作 sort 并转换为 tuple 以获得整个排列:

p = tuple(sorted(
    [tuple(sorted(perm[i:i + 2])) for i in range(0, len(perm), 2)]))

然后将排列添加到set,这将删除重复项:

perms.add(p)

并打印结果:

for perm in sorted(perms):
    print(perm)

【讨论】:

  • 但是你会有重复的。 “A1A2,B1B2”与“A2A1,B2B1”相同。对于 4 个节点,只有 '12, 34', '13, 24', '14, 23' 是唯一的。采取所有排列然后过滤似乎数量级太慢。 (但我也不知道如何有效地做到这一点)
  • 这个可以,但是太慢了 :-) 在每 944 次尝试后它也会挂起很长一段时间。我猜那是它通过“反向”对的时候。
  • @dccharacter,它可能很慢,但写起来很快,有一个工作参考很好,同时努力让它更快。 :-)
  • 哦,这绝对是真的,我非常感谢!我试图弄清楚它是如何工作的:-D
  • 所以我只是添加了一些工作原理的解释。希望它有用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-29
  • 2019-08-29
  • 1970-01-01
  • 2021-05-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多