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

如何在Python+puan网球队选队模型中融入排名实现最优选队?

网球队选队优化问题解决方案

问题背景

每年需为网球队组队参赛,已用Python结合puan工具包实现基础选队规则:

  • 5名候选球员,需选出2名单打、2名双打球员
  • 球员不能同时参加单打和双打
  • 已实现基础规则的代码,但需:
    1. 融入球员单打/双打排名(数值越低实力越强),自动选出最强阵容
    2. 支持后续添加外籍球员数量限制、主客场适配等可扩展规则

一、融入排名实现最优阵容

核心思路是最小化总实力评分:因为排名越低代表实力越强,所以将2名单打球员的单打排名之和 + 2名双打球员的双打排名之和作为目标函数,求解最小值即可得到最强阵容。

修改后的代码实现

import puan
import puan.logic.plog as pg
import puan.modules.configurator as cc
import puan_solvers as ps

# 球员排名数据(数值越低实力越强)
players = {
    "p1": {"s_rank": 1.9225, "d_rank": 1.8000},
    "p2": {"s_rank": 3.8156, "d_rank": 3.7765},
    "p3": {"s_rank": 4.0611, "d_rank": 2.3133},
    "p4": {"s_rank": 3.0974, "d_rank": 3.9666},
    "p5": {"s_rank": 2.3790, "d_rank": 2.5672}
}

# 定义布尔变量:球员是否参加单打/双打
p1s = puan.variable(id="p1s")
p2s = puan.variable(id="p2s")
p3s = puan.variable(id="p3s")
p4s = puan.variable(id="p4s")
p5s = puan.variable(id="p5s")
 
p1d = puan.variable(id="p1d")
p2d = puan.variable(id="p2d")
p3d = puan.variable(id="p3d")
p4d = puan.variable(id="p4d")
p5d = puan.variable(id="p5d")

# 基础约束规则
# 恰好2名单打球员
singles_exact = pg.Exactly(propositions=[p1s,p2s,p3s,p4s,p5s], value=2)
# 恰好2名双打球员
doubles_exact = pg.Exactly(propositions=[p1d,p2d,p3d,p4d,p5d], value=2)
# 球员不能兼项
no_double_duty = [pg.Not(pg.All(p_s, p_d)) for p_s, p_d in zip([p1s,p2s,p3s,p4s,p5s], [p1d,p2d,p3d,p4d,p5d])]

# 构建选队模型
team_model = cc.StingyConfigurator(
    singles_exact,
    doubles_exact,
    *no_double_duty
)

# 定义目标函数:最小化总排名(单打球员s_rank之和 + 双打球员d_rank之和)
objective = (
    p1s * players["p1"]["s_rank"] + p2s * players["p2"]["s_rank"] + p3s * players["p3"]["s_rank"] + 
    p4s * players["p4"]["s_rank"] + p5s * players["p5"]["s_rank"] +
    p1d * players["p1"]["d_rank"] + p2d * players["p2"]["d_rank"] + p3d * players["p3"]["d_rank"] +
    p4d * players["p4"]["d_rank"] + p5d * players["p5"]["d_rank"]
)

# 求解最优解(最小化目标函数)
optimal_solution = next(
    team_model.select(
        solver=ps.glpk_solver,
        only_leafs=True,
        minimize=objective  # 指定最小化目标
    )
)

print("最优阵容:")
for player, status in optimal_solution.items():
    if status == 1.0:
        role = "单打" if player.endswith("s") else "双打"
        print(f"{player[:2]} 参加 {role}")
print("\n原始解数据:", optimal_solution)

代码说明

  • 用pg.Exactly替代原有的AtMost+AtLeast,简化"恰好N人参赛"的约束定义
  • 目标函数将球员参赛状态(0/1)与对应排名相乘后求和,通过最小化该值确保选出实力最强的组合
  • 通过minimize=objective参数将目标函数传入求解器,自动计算最优解

二、扩展规则支持外籍球员、主客场适配

所有扩展规则都通过向cc.StingyConfigurator添加新约束或调整目标函数实现,保持模型的可扩展性。

1. 外籍球员数量限制

假设每个球员新增is_foreign属性,要求参赛球员中外籍人数不超过1人:

# 新增球员属性:是否为外籍
players = {
    "p1": {"s_rank": 1.9225, "d_rank": 1.8000, "is_foreign": False},
    "p2": {"s_rank": 3.8156, "d_rank": 3.7765, "is_foreign": True},
    "p3": {"s_rank": 4.0611, "d_rank": 2.3133, "is_foreign": False},
    "p4": {"s_rank": 3.0974, "d_rank": 3.9666, "is_foreign": True},
    "p5": {"s_rank": 2.3790, "d_rank": 2.5672, "is_foreign": False}
}

# 定义外籍球员参赛总人数约束:不超过1人
foreign_players = [
    p1s * players["p1"]["is_foreign"] + p1d * players["p1"]["is_foreign"],
    p2s * players["p2"]["is_foreign"] + p2d * players["p2"]["is_foreign"],
    p3s * players["p3"]["is_foreign"] + p3d * players["p3"]["is_foreign"],
    p4s * players["p4"]["is_foreign"] + p4d * players["p4"]["is_foreign"],
    p5s * players["p5"]["is_foreign"] + p5d * players["p5"]["is_foreign"]
]
foreign_limit = pg.AtMost(propositions=foreign_players, value=1)

# 将新约束加入模型
team_model = cc.StingyConfigurator(
    singles_exact,
    doubles_exact,
    *no_double_duty,
    foreign_limit
)

2. 主客场适配规则

方式1:优先选择主场表现好的球员(调整目标函数)

给主场适配的球员排名加权重,比如主场时本地球员的排名乘以0.8(相当于实力提升):

# 假设当前为主场,本地球员排名权重0.8
home_weight = 0.8
objective = (
    p1s * players["p1"]["s_rank"] * (home_weight if not players["p1"]["is_foreign"] else 1) +
    p2s * players["p2"]["s_rank"] * (home_weight if not players["p2"]["is_foreign"] else 1) +
    p3s * players["p3"]["s_rank"] * (home_weight if not players["p3"]["is_foreign"] else 1) +
    p4s * players["p4"]["s_rank"] * (home_weight if not players["p4"]["is_foreign"] else 1) +
    p5s * players["p5"]["s_rank"] * (home_weight if not players["p5"]["is_foreign"] else 1) +
    p1d * players["p1"]["d_rank"] * (home_weight if not players["p1"]["is_foreign"] else 1) +
    p2d * players["p2"]["d_rank"] * (home_weight if not players["p2"]["is_foreign"] else 1) +
    p3d * players["p3"]["d_rank"] * (home_weight if not players["p3"]["is_foreign"] else 1) +
    p4d * players["p4"]["d_rank"] * (home_weight if not players["p4"]["is_foreign"] else 1) +
    p5d * players["p5"]["d_rank"] * (home_weight if not players["p5"]["is_foreign"] else 1)
)

方式2:强制规则(如主场必须至少1名本地球员)

# 定义主场约束:至少1名本地球员参赛
local_players_in_match = [
    p1s * (not players["p1"]["is_foreign"]) + p1d * (not players["p1"]["is_foreign"]),
    p2s * (not players["p2"]["is_foreign"]) + p2d * (not players["p2"]["is_foreign"]),
    p3s * (not players["p3"]["is_foreign"]) + p3d * (not players["p3"]["is_foreign"]),
    p4s * (not players["p4"]["is_foreign"]) + p4d * (not players["p4"]["is_foreign"]),
    p5s * (not players["p5"]["is_foreign"]) + p5d * (not players["p5"]["is_foreign"])
]
home_local_requirement = pg.AtLeast(propositions=local_players_in_match, value=1)

# 将约束加入模型
team_model = cc.StingyConfigurator(
    singles_exact,
    doubles_exact,
    *no_double_duty,
    home_local_requirement
)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 13:15:32