python – 生成所有唯一的对排列

我需要生成所有可能的配对,但是约束条件是特定配对仅在结果中出现一次.例如:

import itertools

for perm in itertools.permutations(range(9)):
    print zip(perm[::2], perm[1::2])

生成所有可能的双配对排列;这是输出的一小部分:

...
[(8, 4), (7, 6), (5, 3), (0, 2)]
[(8, 4), (7, 6), (5, 3), (1, 0)]
[(8, 4), (7, 6), (5, 3), (1, 2)]
[(8, 4), (7, 6), (5, 3), (2, 0)]
[(8, 4), (7, 6), (5, 3), (2, 1)]
[(8, 5), (0, 1), (2, 3), (4, 6)]
[(8, 5), (0, 1), (2, 3), (4, 7)]
[(8, 5), (0, 1), (2, 3), (6, 4)]
[(8, 5), (0, 1), (2, 3), (6, 7)]
[(8, 5), (0, 1), (2, 3), (7, 4)]
[(8, 5), (0, 1), (2, 3), (7, 6)]
[(8, 5), (0, 1), (2, 4), (3, 6)]
[(8, 5), (0, 1), (2, 4), (3, 7)]
[(8, 5), (0, 1), (2, 4), (6, 3)]
...

我如何进一步过滤它,以便我只看到(8,4)一次(在所有过滤的排列中),和(8,5)只有一次,而(0,1)只有一次,和(4,7) )只有一次等?

基本上我想要排列使得每个双元素配对只发生一次.

我敢打赌,有一个额外的itertool可以解决这个问题,但我不够专业,不知道它是什么.

更新:Gareth Rees是正确的 – 我完全没有意识到我正试图解决循环问题.我有一个额外的约束,就是我正在做的是将人们分组编程练习.因此,如果我有一个奇数的人,我需要创建一个三人小组,每个练习包括一个奇怪的人.我目前的想法是(1)通过加入一个看不见的人来造成一定数量的人.然后,在配对之后,找到与隐形人配对的人并随机将它们放入现有组中以形成三人小组.但是,我想知道是否还没有一个算法或调整循环,以更好的方式做到这一点.

更新2:Theodros的解决方案产生了完全正确的结果,没有我上面描述的不优雅的未来.每个人都非常乐于助人.

最佳答案
我想分享一个使用标准库中的deque-data结构的循环调度的不同实现:

from collections import deque

def round_robin_even(d, n):
    for i in range(n - 1):
        yield [[d[j], d[-j-1]] for j in range(n/2)]
        d[0], d[-1] = d[-1], d[0]
        d.rotate()

def round_robin_odd(d, n):
    for i in range(n):
        yield [[d[j], d[-j-1]] for j in range(n/2)]
        d.rotate()

def round_robin(n):
    d = deque(range(n))
    if n % 2 == 0:
        return list(round_robin_even(d, n))
    else:
        return list(round_robin_odd(d, n))


print round_robin(5)
  [[[0, 4], [1, 3]],
   [[4, 3], [0, 2]],
   [[3, 2], [4, 1]],
   [[2, 1], [3, 0]],
   [[1, 0], [2, 4]]]


print round_robin(2)
   [[[0, 1]]]

它将对象(此处为int)放在双端队列中.然后它旋转并构建从两端朝向中间的连续对.人们可以想象这就像在中间折叠自己的双端队列.说清楚:

表壳不均匀元素:

 round 1     round 2       # pairs are those numbers that sit
----------  ---------      # on top of each other
0 1 2 3 4   8 0 1 2 3
8 7 6 5     7 6 5 4

在偶数元素的情况下,需要额外的步骤.
(我错过了第一次因为我只检查了不均匀的情况.这产生了一个非常错误的算法……这表明在实现算法时检查边缘情况是多么重要……)
这一特殊步骤是我在每次旋转之前交换两个最左边的元素(它们是双端队列的第一个和最后一个元素) – 这意味着0始终保持在左上角.

案例甚至元素:

 round 1     round 2       # pairs are those numbers that sit
----------  ---------      # on top of each other
0 1 2 3     0 7 1 2
7 6 5 4     6 5 4 3

令我困扰的是这个版本是代码重复的数量,但我找不到改进的方法,同时保持它的可读性.这是我的第一个实现,它的可读性较低IMO:

def round_robin(n):
    is_even = (n % 2 == 0)
    schedule = []
    d = deque(range(n))
    for i in range(2 * ((n - 1) / 2) + 1):
        schedule.append(
                        [[d[j], d[-j-1]] for j in range(n/2)])
        if is_even:
            d[0], d[-1] = d[-1], d[0]
        d.rotate()
    return schedule

更新以解释更新的问题:

要允许三组中的不均匀情况,您只需要更改round_robin_odd(d,n):

def round_robin_odd(d, n):
    for i in range(n):
        h = [[d[j], d[-j-1]] for j in range(n/2)]
        h[-1].append(d[n/2])
        yield h
        d.rotate()

这给出了:

print round_robin(5)
[[[0, 4], [1, 3, 2]],
 [[4, 3], [0, 2, 1]],
 [[3, 2], [4, 1, 0]],
 [[2, 1], [3, 0, 4]],
 [[1, 0], [2, 4, 3]]]

转载注明原文:python – 生成所有唯一的对排列 - 代码日志