You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

英超20队双循环赛程算法故障:无法生成每周10场完整赛程

问题描述

开发英超(EPL)20支球队的双循环赛程生成算法,需求如下:

  • 所有球队两两进行主客场对战,总计380场比赛
  • 每周安排10场比赛,每队每周仅参赛1场,共38周
  • 赛程无重复

当前实现逻辑:先生成全部380种可能的赛程,再逐周筛选未使用且两队当周未参赛的赛程,但结果异常:前6周每周10场,后续9周每周8场,再3周每周10场,最后2周仅4场。

附当前实现代码:

teams = ["ARS", "AVL", "BRE", "BOU", "BRI",
        "BUR", "CHE", "CRY", "FUL",
        "EVE", "LEE", "LEI", "LIV", "MCI", "MUN",
        "NEW", "TOT", "WHU", "WOL", "WAT"]

# 生成全部380种可能的赛程,len(fixtures)为380
fixtures =  [f"{home} - {away}" for home in teams for away in teams if home != away]

# 存储各周赛程
match_weeks = []

# 存储已使用的赛程
s_fixtures = []

# 生成38周赛程
for i in range(38):
    match_week = [] # 每周应恰好10场比赛
    s_teams = [] # 当周已参赛的球队
    for fixture in fixtures:
        if len(match_week) == 10: 
            break
        if fixture not in s_fixtures:
            home, away = fixture[:3], fixture[6:]
            if home not in s_teams and away not in s_teams:
                s_teams.extend([home, away])
                match_week.append(fixture)
                s_fixtures.append(fixture)
    match_weeks.append(match_week)
问题根源
  1. 贪心遍历顺序导致配对死锁:代码按fixtures的原始固定顺序选择赛程,先选中的赛程会占用对应球队,后续可能出现剩余球队无法找到未使用的对手配对,导致凑不满10场。比如部分球队提前用完了所有可选对手,剩下的球队无赛可排。
  2. 无回溯机制:当前是单向贪心选择,一旦选定某场赛程就直接锁定,没有尝试其他组合来确保每周能凑齐10场,这种策略极易在赛程中期陷入无法配对的困境。
  3. 缺乏全局赛程规划:没有考虑每支球队剩余的赛程数量,盲目按顺序选赛程,导致球队的赛程分配失衡,最终无法完成每周的满额比赛安排。
修复方案

方案一:改进贪心策略(简单调整)

  • 每次选择赛程时,优先挑选剩余赛程最少的球队,避免某支球队提前无赛可排
  • 打乱fixtures的遍历顺序(比如每次随机洗牌),避免固定顺序导致的死锁问题
  • 示例调整代码片段:
import random

# 每次生成周赛前打乱fixtures顺序
random.shuffle(fixtures)
for fixture in fixtures:
    # 原逻辑不变...

方案二:采用标准双循环赛制生成法(更可靠)

双循环赛程有成熟的轮转生成算法,先生成单循环(19周),再通过交换主客场得到另一半赛程,完美满足需求:

teams = ["ARS", "AVL", "BRE", "BOU", "BRI",
        "BUR", "CHE", "CRY", "FUL",
        "EVE", "LEE", "LEI", "LIV", "MCI", "MUN",
        "NEW", "TOT", "WHU", "WOL", "WAT"]
team_count = len(teams)
match_weeks = []

# 生成单循环赛程(19周)
for week in range(team_count - 1):
    current_week = []
    # 固定首支球队,其余球队轮转配对
    for i in range(team_count // 2):
        if i == 0:
            home, away = teams[0], teams[week + 1]
        else:
            home_idx = (week + i) % (team_count - 1) + 1
            away_idx = (week - i) % (team_count - 1) + 1
            home, away = teams[home_idx], teams[away_idx]
        current_week.append(f"{home} - {away}")
    match_weeks.append(current_week)

# 生成双循环另一半(交换所有比赛的主客场)
reverse_weeks = []
for week in match_weeks:
    reversed_week = [f"{fixture.split(' - ')[1]} - {fixture.split(' - ')[0]}" for fixture in week]
    reverse_weeks.append(reversed_week)

# 合并得到完整38周赛程
match_weeks += reverse_weeks

# 验证结果(可选)
for idx, week in enumerate(match_weeks, 1):
    print(f"第{idx}周:{len(week)}场比赛")
    used_teams = []
    for fx in week:
        h, a = fx.split(' - ')
        used_teams.extend([h, a])
    assert len(set(used_teams)) == team_count, f"第{idx}周存在重复参赛球队"
assert len(match_weeks) == 38, "总周数不符合要求"
assert sum(len(w) for w in match_weeks) == 380, "总比赛数不符合要求"

该方法的优势:

  • 确保每支球队和其他球队都进行主客场两次对战
  • 天然满足每周10场比赛、每队每周仅赛1场的要求
  • 可额外调整赛程顺序,避免球队连续多场主场/客场(比如打乱后半段赛程的顺序)

内容的提问来源于stack exchange,提问作者Jjenus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 21:50:28