[英]Generating all unique pair permutations
我需要生成所有可能的配对,但是约束条件是特定配对仅在结果中出现一次。 例如:
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的解决方案产生了完全正确的结果,没有我上面描述的不优雅的未来。 每个人都非常乐于助人。
传递列表set
以确保每个元组只存在一次。
>>> from itertools import permutations
>>> set( [ zip( perm[::2], perm[1::2] ) for perm in permutations( range( 9 ) ) ] )
set([(7, 3), (4, 7), (1, 3), (4, 8), (5, 6), (2, 8), (8, 0), (3, 2), (2, 1), (6, 2), (1, 6), (5, 1), (3, 7), (2, 5), (8, 5), (0, 3), (5, 8), (4, 0), (1, 2), (3, 8), (3, 1), (6, 7), (2, 0), (8, 1), (7, 6), (3, 0), (6, 3), (1, 5), (7, 2), (3, 6), (0, 4), (8, 6), (3, 5), (4, 1), (6, 4), (5, 4), (2, 6), (8, 2), (2, 7), (7, 1), (4, 5), (8, 3), (1, 4), (6, 0), (7, 5), (2, 3), (0, 7), (8, 7), (4, 2), (1, 0), (0, 8), (6, 5), (4, 6), (0, 1), (5, 3), (7, 0), (6, 8), (3, 4), (6, 1), (5, 7), (5, 2), (0, 2), (7, 4), (0, 6), (1, 8), (4, 3), (1, 7), (0, 5), (5, 0), (7, 8), (2, 4), (8, 4)])
根据您的描述,您希望上述range( 9 )
的每个2元组排列都能根据您的代码为您提供所有不同的排列。 但是,这是非常低效的。
但是,您可以通过执行以下操作进一步简化代码:
>>> list( permutations( range( 9 ), 2 ) )
[(0, 1), (0, 2), (0, 3), (0, 4), (0, 5), (0, 6), (0, 7), (0, 8), (1, 0), (1, 2), (1, 3), (1, 4), (1, 5), (1, 6), (1, 7), (1, 8), (2, 0), (2, 1), (2, 3), (2, 4), (2, 5), (2, 6), (2, 7), (2, 8), (3, 0), (3, 1), (3, 2), (3, 4), (3, 5), (3, 6), (3, 7), (3, 8), (4, 0), (4, 1), (4, 2), (4, 3), (4, 5), (4, 6), (4, 7), (4, 8), (5, 0), (5, 1), (5, 2), (5, 3), (5, 4), (5, 6), (5, 7), (5, 8), (6, 0), (6, 1), (6, 2), (6, 3), (6, 4), (6, 5), (6, 7), (6, 8), (7, 0), (7, 1), (7, 2), (7, 3), (7, 4), (7, 5), (7, 6), (7, 8), (8, 0), (8, 1), (8, 2), (8, 3), (8, 4), (8, 5), (8, 6), (8, 7)]
方法permutations
也采用一个长度参数,允许您指定返回的元组的长度。 所以,你使用了正确的itertool
提供的函数,但是错过了元组长度参数。
我想分享使用标准库中的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]]]
正如MatthieuW在这个答案中所说的那样,看起来好像你正试图为循环赛锦标赛制定赛程 。 这可以使用这种算法轻松生成,主要困难是处理奇数队(当每队在一轮中获得一次再见时)。
def round_robin_schedule(n):
"""
Generate a round-robin tournament schedule for `n` teams.
"""
m = n + n % 2 # Round up to even number.
for r in xrange(m - 1):
def pairing():
if r < n - 1:
yield r, n - 1
for i in xrange(m // 2 - 1):
p, q = (r + i + 1) % (m - 1), (m + r - i - 2) % (m - 1)
if p < n - 1 and q < n - 1:
yield p, q
yield list(pairing())
例如,有九个团队:
>>> list(round_robin_schedule(9))
[[(0, 8), (2, 7), (3, 6), (4, 5)],
[(1, 8), (2, 0), (4, 7), (5, 6)],
[(2, 8), (3, 1), (4, 0), (6, 7)],
[(3, 8), (4, 2), (5, 1), (6, 0)],
[(4, 8), (5, 3), (6, 2), (7, 1)],
[(5, 8), (6, 4), (7, 3), (0, 1)],
[(6, 8), (7, 5), (0, 3), (1, 2)],
[(7, 8), (0, 5), (1, 4), (2, 3)],
[(0, 7), (1, 6), (2, 5), (3, 4)]]
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.