簡體   English   中英

生成所有唯一的對排列

[英]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提供的函數,但是錯過了元組長度參數。

itertools.permutations文檔

我想分享使用標准庫中的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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM