如何编写高效的配对算法?

Wol*_*rcy 5 python algorithm backtracking tournament

我需要一种算法的帮助,该算法可以有效地将人们分组,并确保以前的配对不会重复。

例如,假设我们有 10 位候选人;

candidates = [0,1,2,3,4,5,6,7,8,9]

并假设我们有一个先前匹配的字典,使得每个键值对 iecandidate:matches代表一个候选者和迄今为止与它们配对的候选者数组;

prev_matches = {0: [6, 5, 1, 2], 1: [4, 9, 0, 7], 2: [9, 8, 6, 0], 3: [5, 4, 8, 9], 4: [1, 3, 9, 6], 5: [3, 0, 7, 8], 6: [0, 7, 2, 4], 7: [8, 6, 5, 1], 8: [7, 2, 3, 5], 9: [2, 1, 4, 3]}

因此,对于Candidate 0,它们首先与 、 、 和 配对,在随后的配对轮中,它们与、、 和Candidate 6配对。字典中的其他键值对也是如此。Candidate 5Candidate 1Candidate 2

从所有比赛的时长可以看出,已经进行了四轮比赛prev_matches。如何编写一个算法来创建第五、第六...第 n(最多 numberOfCandidates - 1)轮比赛,​​以使候选人没有重复对?

因此Candidate 0不能再与Candidate 6Candidate 5Candidate 1和配对Candidate 2。在可能的第五轮比赛之后,我们可以 prev_matches这样:

prev_matches: {0: [6, 5, 1, 2, 3], 1: [4, 9, 0, 7, 2], 2: [9, 8, 6, 0, 1], 3: [5, 4, 8, 9, 0], 4: [1, 3, 9, 6, 7], 5: [3, 0, 7, 8, 9], 6: [0, 7, 2, 4, 8], 7: [8, 6, 5, 1, 4], 8: [7, 2, 3, 5, 8], 9: [2, 1, 4, 3, 5]}

这是我尝试过的一个简单的解决方案:

def make_match(prev_matches):
    paired_candidates = set()
    for candidate, matches in prev_matches.items():
        i = 0
        while i < 10:
            if i != candidate and i not in matches and i not in paired_candidates and candidate not in paired_candidates:
                prev_matches[candidate].append(i)
                prev_matches[i].append(candidate)
                paired_candidates.add(candidate)
                paired_candidates.add(i)
                break
            i += 1
    return prev_matches
Run Code Online (Sandbox Code Playgroud)

它在第五轮工作并返回以下结果:

prev_matches = {0: [6, 5, 1, 2, 3], 1: [4, 9, 0, 7, 2], 2: [9, 8, 6 0, 1], 3: [5, 4, 8, 9, 0], 4: [1, 3, 9, 6, 5], 5: [3, 0, 7, 8, 4], 6: [0, 7, 2, 4, 8], 7: [8, 6, 5, 1, 9], 8: [7, 2, 3, 5, 6], 9: [2, 1, 4, 3, 7]}

然而,在第六轮中,它失败了,因为一些候选者(7 和 8)找不到有效的配对:

prev_matches = {0: [6, 5, 1, 2, 3, 4], 1: [4, 9, 0, 7, 2, 3], 2: [9, 8, 6, 0, 1, 5], 3: [5, 4, 8, 9, 0, 1], 4: [1, 3, 9, 6, 5, 0], 5: [3, 0, 7, 8, 4, 2], 6: [0, 7, 2, 4, 8, 9], 7: [8, 6, 5, 1, 9], 8: [7, 2, 3, 5, 6], 9: [2, 1, 4, 3, 7, 6]}

因此,它既不是可靠的也不可接受的解决方案。

我正在考虑将其视为回溯问题,以便我在各轮中探索所有可能的配对,直到在第 n 轮后达到完全可接受且有效的解决方案。但这里关注的是如何使其有效地运作。

如果我能得到任何帮助,我将不胜感激。

Ste*_*tef 3

如果你从一开始就负责比赛,那么最简单的解决方案就是按照循环赛的方式组织配对。

如果您无法控制第一轮的配对,并且必须组织后续轮次,那么这里有一个使用模块 networkx 来计算图中的最大匹配的解决方案:

from networkx import Graph
from networkx.algorithms.matching import max_weight_matching, is_perfect_matching

def next_rounds(candidates, prev_matches):
    G = Graph()
    G.add_nodes_from(candidates)
    G.add_edges_from((u,v) for u,p in prev_matches.items() for v in candidates.difference(p).difference({u}))
    m = max_weight_matching(G)
    while is_perfect_matching(G, m):
        yield m
        G.remove_edges_from(m)
        m = max_weight_matching(G)

for r in next_rounds({0,1,2,3,4,5,6,7,8,9},
                     {0: [6, 5, 1, 2], 1: [4, 9, 0, 7], 2: [9, 8, 6, 0], 3: [5, 4, 8, 9], 4: [1, 3, 9, 6],  5: [3, 0, 7, 8], 6: [0, 7, 2, 4], 7: [8, 6, 5, 1], 8: [7, 2, 3, 5], 9: [2, 1, 4, 3]}):
    print(r)
Run Code Online (Sandbox Code Playgroud)

输出:

{(2, 7), (8, 1), (0, 9), (4, 5), (3, 6)}
{(2, 4), (3, 7), (8, 0), (9, 5), (1, 6)}
{(0, 7), (8, 4), (1, 5), (9, 6), (2, 3)}
{(9, 7), (0, 4), (8, 6), (2, 5), (1, 3)}
{(1, 2), (0, 3), (8, 9), (5, 6), (4, 7)}
Run Code Online (Sandbox Code Playgroud)