简体   繁体   English

生成所有唯一的对排列

[英]Generating all unique pair permutations

I need to generate all possible pairings, but with the constraint that a particular pairing only occurs once in the results. 我需要生成所有可能的配对,但是约束条件是特定配对仅在结果中出现一次。 So for example: 例如:

import itertools

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

generates all possible two-paired permutations; 生成所有可能的双配对排列; here's a small subset of the output: 这是输出的一小部分:

...
[(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)]
...

How do I further filter it so that I only ever see (8,4) once (throughout all of the filtered permutations), and (8,5) only once, and (0,1) only once, and (4,7) only once, etc.? 我如何进一步过滤它,以便我只看到(8,4)一次(在所有过滤的排列中),和(8,5)只有一次,而(0,1)只有一次,和(4,7) )只有一次等?

Basically I want the permutations such that each two-element pairing happens only once. 基本上我想要排列使得每个双元素配对只发生一次。

I'll bet there's an additional itertool that would solve this but I'm not expert enough to know what it is. 我敢打赌,有一个额外的itertool可以解决这个问题,但我不够专业,不知道它是什么。

Update : Gareth Rees is correct -- I was completely unaware that I was trying to solve the round-robin problem. 更新 :Gareth Rees是正确的 - 我完全没有意识到我正试图解决循环问题。 I have an additional constraint which is that what I'm doing is grouping people for pair-programming exercises. 我有一个额外的约束,那就是我正在做的是将人们分组编程练习。 Thus, if I have an odd number of people, I need to create a group of three to include an odd person for each exercise. 因此,如果我有一个奇数的人,我需要创建一个三人小组,每个练习包括一个奇怪的人。 My current thinking is to (1) make an even number of people by adding in an invisible person. 我目前的想法是(1)通过加入一个看不见的人来造成一定数量的人。 Then, after the pairing, find the person paired with the invisible person and randomly place them into an existing group to form a team of three. 然后,在配对之后,找到与隐形人配对的人并随机将它们放入现有组中以形成三人小组。 However, I wonder if there isn't already an algorithm or adjustment to round-robin that does this in a better way. 但是,我想知道是否还没有一个算法或调整循环,以更好的方式做到这一点。

Update 2 : Theodros' solution produces exactly the right result without the inelegant futzing about I describe above. 更新2 :Theodros的解决方案产生了完全正确的结果,没有我上面描述的不优雅的未来。 Everyone's been amazingly helpful. 每个人都非常乐于助人。

Pass the list to set to get make sure each tuple only exists once. 传递列表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)])

From your description you want each of the 2-tuple permutations of the range( 9 ) the above should give you all of the various permutations, based on your code. 根据您的描述,您希望上述range( 9 )的每个2元组排列都能根据您的代码为您提供所有不同的排列。 But, this is pretty inefficient. 但是,这是非常低效的。

However you can further simplify your code by doing the following: 但是,您可以通过执行以下操作进一步简化代码:

>>> 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)]

The method permutations also takes a length argument that will allow you to specify the length of the tuple returned. 方法permutations也采用一个长度参数,允许您指定返回的元组的长度。 So, you were using the correct itertool provided function, but missed the tuple length parameter. 所以,你使用了正确的itertool提供的函数,但是错过了元组长度参数。

itertools.permutations documentation itertools.permutations文档

I'd like to share a different implementation of round-robin scheduling that makes use of the deque -data structure from the Standard Library: 我想分享使用标准库中的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]]]

It puts the objects(ints here) in the deque. 它将对象(此处为int)放在双端队列中。 Then it rotates and builds consecutive pairs taking from both ends towards the middle. 然后它旋转并构建从两端朝向中间的连续对。 One can imagine this as folding the deque in the middle back on itself. 人们可以想象这就像在中间折叠自己的双端队列。 To make it clear: 说清楚:

Case uneven elements : 表壳不均匀元素

 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

In case of even elements an additional step is required. 在偶数元素的情况下,需要额外的步骤。
(I missed the first time cause I only checked the uneven case. This yielded a horribly wrong algorithm... which shows me how important it is to check edge cases when implementing an algorithm...) (我错过了第一次因为我只检查了不均匀的情况。这产生了一个非常错误的算法......这表明在实现算法时检查边缘情况是多么重要......)
This special step is that I swap the two leftmost elements (which are the first and last elements of the deque) before each rotation -- this means the 0 stays all the time upper left. 这一特殊步骤是我在每次旋转之前交换两个最左边的元素(它们是双端队列的第一个和最后一个元素) - 这意味着0始终保持在左上角。

Case even elements: 案例甚至元素:

 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

What haunts me about this version is the amount of code duplication, but I couldn't find a way to improve while keeping it as readable. 令我困扰的是这个版本是代码重复的数量,但我找不到改进的方法,同时保持它的可读性。 Here's my first implementation, which is less readable IMO: 这是我的第一个实现,它的可读性较低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

Update to account for the updated question: 更新以解释更新的问题:

To allow in the uneven case for groups of three you just need to change round_robin_odd(d, n) : 要允许三组中的不均匀情况,您只需要更改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()

This gives: 这给出了:

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]]]

As MatthieuW says in this answer , it looks as if you are trying to generate a schedule for a round-robin tournament . 正如MatthieuW在这个答案中所说的那样,看起来好像你正试图为循环赛锦标赛制定赛程 This can be easily generated using this algorithm , the main difficulty being the handling of an odd number of teams (when each team gets a bye in one round). 这可以使用这种算法轻松生成,主要困难是处理奇数队(当每队在一轮中获得一次再见时)。

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())

For example, with nine teams: 例如,有九个团队:

>>> 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM