从组合列表中选择对的最佳策略

时间:2013-08-11 14:09:24

标签: python list combinations

问题:有人可以帮我弄清楚如何计算具有最大对数的循环(每个循环三个 - 参见上一个例子)?

这就是我想要做的事:
- >每个周期对两个用户进行配对      - 每个用户仅与给定周期中的其他用户配对一次      - 每个用户仅在所有周期中与其他所有用户配对一次

现实世界:
你每周都会从名单中遇到一个新人(周=周期) 你再也不会遇到同一个人了 每个用户每周都与其他人匹配

这是我的问题:
我能够创建用户组合并选择从未遇到过的用户对。但是,有时我只能在一个周期中匹配两对而不是三对。因此, 我正在寻找一种从组合列表中创建最佳选择的方法。

1)我从6个用户开始:

users = ["A","B","C","D","E","F"]

2)从这个列表中,我创建了可能的组合:

 x = itertools.combinations(users,2)
    for i in x:
        candidates.append(i)

这给了我:

 .   A,B  A,C  A,D A,E A,F
 .    .   B,C  B,D B,E B,F
 .    .    .   C,D C,E C,F
 .    .    .    .  D,E D,F
 .    .    .    .   .  E,F

   candidates = [('A', 'B'), ('A', 'C'), ('A', 'D'), ('A', 'E'), ('A', 'F'), ('B', 'C'), 
       ('B', 'D'), ('B', 'E'), ('B', 'F'), ('C', 'D'), ('C', 'E'), ('C', 'F'), 
       ('D', 'E'), ('D', 'F'), ('E', 'F')]

3)现在,我想从这个列表中选择对,这样用户(A到F)只出现一次&所有用户都与此周期中的某人配对

示例:

cycle1 = ('A','B'),('C','D') ('E','F')

下一个周期,我想找到另外一组三对。

我计算了6个用户应该有5个周期,每个3对:

示例:

cycle 1: AF BC DE
cycle 2: AB CD EF
cycle 3: AC BE DF
cycle 4: AE BD CF
cycle 5: AD BF CE

有人可以帮我弄清楚如何计算具有最大对数的循环(每个循环三个 - 参见上一个例子)?

3 个答案:

答案 0 :(得分:3)

与评论中提到的Whatang一样,您的问题实际上等同于创建round-robin style tournament的问题。这是维基百科页面上提到的算法的Python版本,另请参阅thisthis answer.

def schedule(users):
    # first make copy or convert to list with length `n`
    users = list(users)  
    n = len(users)

    # add dummy for uneven number of participants
    if n % 2:  
        users.append('_')
        n += 1

    cycles = []
    for _ in range(n-1):
        # "folding", `//` for integer division
        c = zip(users[:n//2], reversed(users[n//2:]))
        cycles.append(list(c))

        # rotate, fixing user 0 in place
        users.insert(1, users.pop())
    return cycles

schedule(['A', 'B', 'C', 'D', 'E', 'F'])

对于您的示例,它会产生以下内容:

[[('A', 'F'), ('B', 'E'), ('C', 'D')],
 [('A', 'E'), ('F', 'D'), ('B', 'C')],
 [('A', 'D'), ('E', 'C'), ('F', 'B')],
 [('A', 'C'), ('D', 'B'), ('E', 'F')],
 [('A', 'B'), ('C', 'F'), ('D', 'E')]]

答案 1 :(得分:1)

这是一个基于itertools的解决方案:

import itertools

def hasNoRepeats(matching):
    flattenedList = list(itertools.chain.from_iterable(matching))
    flattenedSet = set(flattenedList)
    return len(flattenedSet) == len(flattenedList)

def getMatchings(users, groupSize=2):
#   Get all possible pairings of users
    pairings = list(itertools.combinations(users, groupSize))
#   Get all possible groups of pairings of the correct size, then filter to eliminate groups of pairings where a user appears more than once
    possibleMatchings = filter(hasNoRepeats, itertools.combinations(pairings, len(users)/groupSize))
#   Select a series of the possible matchings, making sure no users are paired twice, to create a series of matching cycles.
    cycles = [possibleMatchings.pop(0)]
    for matching in possibleMatchings:
        # pairingsToDate represents a flattened list of all pairs made in cycles so far
        pairingsToDate = list(itertools.chain.from_iterable(cycles))
        # The following checks to make sure there are no pairs in matching (the group of pairs being considered for this cycle) that have occurred in previous cycles (pairingsToDate)
        if not any([pair in pairingsToDate for pair in matching]):
            # Ok, 'matching' contains only pairs that have never occurred so far, so we'll add 'matching' as the next cycle
            cycles.append(matching)
    return cycles

# Demo:

users = ["A","B","C","D","E","F"]

matchings = getMatchings(users, groupSize=2)

for matching in matchings:
    print matching

输出:

(('A', 'B'), ('C', 'D'), ('E', 'F'))
(('A', 'C'), ('B', 'E'), ('D', 'F'))
(('A', 'D'), ('B', 'F'), ('C', 'E'))
(('A', 'E'), ('B', 'D'), ('C', 'F'))
(('A', 'F'), ('B', 'C'), ('D', 'E'))

Python 2.7。这有点暴力,但它完成了工作。

答案 2 :(得分:0)

好的这是伪代码,但它应该可以解决问题

while length(candidates) > length(users)/2 do
{
    (pairs, candidates) = selectPairs(candidates, candidates)
    if(length(pairs) == length(users)/2)
        cycles.append(pairs)
}

selectPairs(ccand, cand)
{
    if notEmpty(ccand) then
        cpair = cand[0]
        ncand = remove(cpair, cand)
        nccand = removeOccurences(cpair, ncand)
        (pairs, tmp) = selectPairs(nccand, ncand)
        return (pairs.append(cpair), tmp)
    else
        return ([],cand)
}

其中:
remove(x, xs)x中删除xs removeOccurences(x, xs)删除每对xs,其中至少包含一对“x

中的一个元素

编辑:停止算法的条件可能需要进一步考虑......