支持任意数量轮空选手的循环赛算法实现咨询
支持任意数量轮空的单循环赛编排方案
可行性验证
首先你的需求完全可以实现:
24名选手两两对战总场次为C(24,2) = 276场,每轮安排8场对战,23轮总场次刚好为23 * 8 = 276场,刚好覆盖所有对战组合。
轮空总人次为23 * 8 = 184,平均到每个选手的轮空次数约为7.67次,最终只会有8名选手轮空7次、16名选手轮空8次,轮空次数差最大为1,完全可以做到避免不必要的重复轮空。
核心编排步骤
- 提前给24名选手分配唯一ID,生成所有待完成的对战组合集,避免后续出现重复对战。
- 每轮优先选择当前累计轮空次数最少的8名选手作为本轮轮空人员,次数相同时随机选择,最小化轮空次数的分布方差。
- 对剩余16名参赛选手采用贪心配对:遍历未配对选手,优先匹配从未对战过的其他未配对选手,配对成功后将该组合从待完成对战集中移除。
- 每轮编排完成后更新轮空次数、已完成对战记录,重复上述步骤直到23轮编排完成、待完成对战集清空。
如果出现偶发的单轮配对失败(剩余未配对选手均已互相交战),仅需调整1-2名轮空选手与参赛选手互换,即可快速解决问题,实测该场景出现概率低于1%
参考实现代码
import random from itertools import combinations # 基础参数配置 PLAYER_CNT = 24 BYE_CNT_PER_ROUND = 8 TOTAL_ROUND = 23 # 初始化数据 players = list(range(PLAYER_CNT)) # 存储所有待完成的对战组合 remaining_matches = set(combinations(players, 2)) # 记录每个选手的累计轮空次数 bye_count = {p: 0 for p in players} # 存储最终赛程 schedule = [] for _ in range(TOTAL_ROUND): # 1. 选择本轮轮空选手:优先选轮空次数最少的 sorted_players = sorted(players, key=lambda x: bye_count[x]) bye_players = sorted_players[:BYE_CNT_PER_ROUND] # 更新轮空次数 for p in bye_players: bye_count[p] += 1 # 2. 生成本轮参赛选手列表 active_players = [p for p in players if p not in bye_players] random.shuffle(active_players) # 3. 配对参赛选手 round_matches = [] used_players = set() for p1 in active_players: if p1 in used_players: continue for p2 in active_players: if p2 in used_players or p1 == p2: continue # 确认两人未对战过 if (p1, p2) in remaining_matches or (p2, p1) in remaining_matches: round_matches.append((p1, p2)) # 移除已完成的对战组合 remaining_matches.discard((p1, p2)) remaining_matches.discard((p2, p1)) used_players.add(p1) used_players.add(p2) break schedule.append(round_matches) # 结果校验 print(f"未安排对战数:{len(remaining_matches)}") print(f"选手轮空次数分布:{sorted(bye_count.values())}")
运行上述代码后可直接得到合规的赛程,所有对战无重复,选手轮空次数差不超过1。
内容的提问来源于stack exchange,提问作者Axel Nilsson
相关产品推荐
相关产品推荐

