简体   繁体   English

如何配对列表中的元素而不重复相同的组合?

[英]How to Pair elements from a list without repeating the same combination?

I have a list of elements, that I would like to group (of size 2,3,4 etc.) and find some unique combinations in each iteration.我有一个元素列表,我想对其进行分组(大小为 2、3、4 等)并在每次迭代中找到一些独特的组合。 I have the following snippet, that forms combinations of size group_size of members .我有以下片段,它形成了成员大小group_size的组合。

  1. I would like to know how can I avoid duplicate combinations in the new iterations.我想知道如何避免新迭代中的重复组合。
  2. For group_size > 2, I want to also avoid any two elements of members repeating.对于group_size > 2,我还想避免成员的任何两个元素重复。 Let's say: group_size = 3;假设: group_size = 3; then ['A', 'B', 'C'] is accepted, but any other combination of ['A', 'B',~] or ['B', 'C',~] or ['A', 'C',~] is not accepted in the future iterations, where '~' represents any element other than ['A', 'B', 'C'] .然后['A', 'B', 'C']被接受,但是['A', 'B',~]['B', 'C',~]['A' 的任何其他组合, 'C',~]在以后的迭代中不被接受,其中 '~' 表示除['A', 'B', 'C']之外的任何元素。
import random
from itertools import zip_longest
members = ['A', 'B', 'C', 'D', 'E', 'F', 'U', 'V', 'W', 'X', 'Y', 'Z']
group_size = 2
for i in range(10):
    random.shuffle(members)
    pairs_loc = [iter(members)] * group_size
    pairs = zip_longest(*pairs_loc)
    print(*pairs)

Honestly I'm not sure I understood correctly what you want to do, but let me try, maybe it is useful to you all the same.老实说,我不确定我是否正确理解了您想要做什么,但是让我尝试一下,也许它对您同样有用。

For the first point Python already has what (I believe that) you're looking for: itertools.combinations .对于第一点,Python 已经拥有(我相信)您正在寻找的东西: itertools.combinations

For the second point we need some code.对于第二点,我们需要一些代码。 One note: I'm sure you realize that with this second requirement you will have some cases when not all members appear in at least one combination: eg, with 12 members and a groupsize > 6 .一个注意事项:我相信您已经意识到,根据第二个要求,您会遇到一些情况,即并非所有成员都至少出现在一种组合中:例如,有 12 个成员且groupsize > 6

The code:编码:

def select_combos(members, groupsize):
    assert groupsize > 1
    shuffle(members)
    if groupsize == 2:
        return list(combinations(members, 2))
    finalcombos = []
    usedcombos = []
    for c in combinations(members, groupsize):
        tempcombos = list(combinations(c, 2))
        for c2 in tempcombos:
            if c2 in usedcombos:
                break
        else:
            usedcombos += tempcombos
            finalcombos.append(c)
    return finalcombos

m = ['A', 'B', 'C', 'D', 'E', 'F', 'U', 'V', 'W', 'X', 'Y', 'Z']
select_combos(m, 2)
[('C', 'A'), ('C', 'Z'), ('C', 'Y'), ('C', 'E'), ('C', 'W'), ('C', 'B'), ('C', 'U'), ('C', 'X'), ('C', 'D'), ('C', 'V'), ('C', 'F'), ('A', 'Z'), ('A', 'Y'), ('A', 'E'), ('A', 'W'), ('A', 'B'), ('A', 'U'), ('A', 'X'), ('A', 'D'), ('A', 'V'), ('A', 'F'), ('Z', 'Y'), ('Z', 'E'), ('Z', 'W'), ('Z', 'B'), ('Z', 'U'), ('Z', 'X'), ('Z', 'D'), ('Z', 'V'), ('Z', 'F'), ('Y', 'E'), ('Y', 'W'), ('Y', 'B'), ('Y', 'U'), ('Y', 'X'), ('Y', 'D'), ('Y', 'V'), ('Y', 'F'), ('E', 'W'), ('E', 'B'), ('E', 'U'), ('E', 'X'), ('E', 'D'), ('E', 'V'), ('E', 'F'), ('W', 'B'), ('W', 'U'), ('W', 'X'), ('W', 'D'), ('W', 'V'), ('W', 'F'), ('B', 'U'), ('B', 'X'), ('B', 'D'), ('B', 'V'), ('B', 'F'), ('U', 'X'), ('U', 'D'), ('U', 'V'), ('U', 'F'), ('X', 'D'), ('X', 'V'), ('X', 'F'), ('D', 'V'), ('D', 'F'), ('V', 'F')]

select_combos(m, 5)
[('W', 'V', 'C', 'U', 'E'), ('W', 'A', 'X', 'B', 'F'), ('V', 'A', 'D', 'Y', 'Z')]

EDIT Now that it's clearer, the request for group size 2 is equivalent to scheduling a round-robin tournament, so we can use the standard circle method here.编辑现在更清楚了,对组大小 2 的请求等效于安排循环赛,所以我们可以在这里使用标准的圆方法

One quick and dirty implementation of the rotation:一种快速而肮脏的轮换实现:

def rotate(roster):
    half = (len(roster)+1)//2
    t=roster[1]
    roster[1] = roster[half]
    for j in range(half, len(roster)-1):
        roster[j] = roster[j+1]
    roster[-1] = roster[half-1]
    for j in range(half-1, 1, -1):
        roster[j] = roster[j-1]
    roster[2] = t

    for i in range(half):
        print(f'{roster[i]}-{roster[i+half]}   ', end = '')
    print()

members = ['A', 'B', 'C', 'D', 'E', 'F', 'U', 'V', 'W', 'X', 'Y', 'Z']
shuffle(members)
for r in range(len(members)):
    rotate(members)

At each iteration this will rotate the roster one step and print the pairings.在每次迭代时,这会将花名册旋转一步并打印配对。 Note that at the n-th iteration the roster, and hence the pairings, will be the same as at the start.请注意,在第 n 次迭代时,名册以及配对将与开始时相同。

You can use a dictionary of sets to keep track of the pairings that have already been used in previous groups.您可以使用集合字典来跟踪先前组中已经使用过的配对。 Then assemble a new group based on the eligible members that you constrain with each addition to the group.然后根据您在每次添加组时限制的合格成员组建一个新组。 Note that it is possible to hit a dead-end so your group forming logic needs to be able to reset itself and start over with different random members:请注意,可能会遇到死胡同,因此您的组形成逻辑需要能够自行重置并使用不同的随机成员重新开始:

import random
members = ['A', 'B', 'C', 'D', 'E', 'F', 'U', 'V', 'W', 'X', 'Y', 'Z']

remaining  = {M:set(members)-{M} for M in members} # unused pairs by member
group_size = 3
for _ in range(10):
    more  = set()  # set of members that can be added to group
    group = []     # current group
    while len(group)<group_size:
        if len(more)+len(group)<group_size: # group not feasible
           more  = {m for m,r in remaining.items() if r} # reset
           group = [] 
        m = random.sample(more,1)[0] # select eligible member
        group.append(m)              # add to group 
        more &= remaining[m]         # constrain next members
    print(group)
    for m in group:                  # track unused pairs
        remaining[m].difference_update(group)   
        
['B', 'Z', 'Y']
['X', 'U', 'W']
['Y', 'U', 'C']
['D', 'Y', 'W']
['B', 'X', 'A']
['X', 'E', 'D']
['B', 'V', 'F']
['D', 'V', 'Z']
['E', 'A', 'F']
['A', 'C', 'Z']

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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