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

如何用PuLP实现PGA梦幻体育阵容的最小唯一球员数约束?

问题描述

我正在使用Python的PuLP库构建PGA每日梦幻体育阵容生成程序,当前基础逻辑(变量定义、目标函数、薪资/人数约束)运行正常,代码如下:

# 每个球员对应一个二进制变量(0/1表示是否入选阵容)
# player_dict是存储球员数据的字典,包含Name、Fpts、Salary等字段
lp_variables = {
    player: plp.LpVariable(player, cat="Binary")
    for player, _ in self.player_dict.items()
}

# 目标函数:最大化总得分
self.problem += (
    plp.lpSum(
        self.player_dict[player]["Fpts"] * lp_variables[player]
        for player in self.player_dict
    ),
    "Objective",
)

# 薪资约束
max_salary = 50000 if self.site == "dk" else 60000
min_salary = 0

self.problem += (
    plp.lpSum(
        self.player_dict[player]["Salary"] * lp_variables[player]
        for player in self.player_dict
    )
    <= max_salary
)
self.problem += (
    plp.lpSum(
        self.player_dict[player]["Salary"] * lp_variables[player]
        for player in self.player_dict
    )
    >= self.min_salary
)

# 阵容人数约束:固定6名球员
self.problem += (
    plp.lpSum(lp_variables[player] for player in self.player_dict) >= 6
)
self.problem += (
    plp.lpSum(lp_variables[player] for player in self.player_dict) <= 6
)

# 生成多组阵容
for i in range(self.num_lineups):
    try:
        self.problem.solve(plp.PULP_CBC_CMD(msg=0))
    except plp.PulpSolverError:
        self.simDoc.update({'jobProgressLog': ArrayUnion(['求解器出错 - 问题不可行。仅生成{}套阵容,目标为{}套。继续导出。'.format(len(self.lineups), self.num_lineups)])})
       
    score = str(self.problem.objective)
    for v in self.problem.variables():
        score = score.replace(v.name, str(v.varValue))
        
    player_names = [
        v.name.replace("_", " ")
        for v in self.problem.variables()
        if v.varValue != 0
    ]
    fpts = eval(score)

    self.lineups[fpts] = player_names
    
    # 约束后续阵容得分低于当前最优解,生成次优解
    self.problem += plp.lpSum(
        self.player_dict[player]["Fpts"] * lp_variables[player]
        for player in self.player_dict
    ) <= (fpts - 0.001)

现在需要添加一个可选约束:强制每个新生成的阵容与之前所有阵容相比,至少有指定数量的唯一球员。例如要求至少3个唯一球员时,以下两组阵容是无效的(仅存在2个不同球员):

[Player1, Player4, Player29, Player6, Player10]
[Player1, Player4, Player29, Player88, Player45]

请问如何用PuLP实现该规则?


解决方案

核心思路是限制新阵容与每一个已生成阵容的重叠球员数量:如果要求至少N个唯一球员,那么新阵容与旧阵容的重叠球员数不能超过总人数 - N(比如总人数6,N=3时,重叠最多3人)。

修改后的完整代码

# 初始化时新增:存储已生成阵容的球员选择状态(二进制变量集合)
self.previous_lineups = []
# 可选参数:最小唯一球员数,可根据需求调整
self.min_unique_players = 3
# 阵容固定人数
self.roster_size = 6

# 每个球员对应一个二进制变量(0/1表示是否入选阵容)
lp_variables = {
    player: plp.LpVariable(player, cat="Binary")
    for player, _ in self.player_dict.items()
}

# 目标函数:最大化总得分
self.problem += (
    plp.lpSum(
        self.player_dict[player]["Fpts"] * lp_variables[player]
        for player in self.player_dict
    ),
    "Objective",
)

# 薪资约束
max_salary = 50000 if self.site == "dk" else 60000
min_salary = 0

self.problem += (
    plp.lpSum(
        self.player_dict[player]["Salary"] * lp_variables[player]
        for player in self.player_dict
    )
    <= max_salary
)
self.problem += (
    plp.lpSum(
        self.player_dict[player]["Salary"] * lp_variables[player]
        for player in self.player_dict
    )
    >= self.min_salary
)

# 阵容人数约束:固定6名球员
self.problem += (
    plp.lpSum(lp_variables[player] for player in self.player_dict) >= self.roster_size
)
self.problem += (
    plp.lpSum(lp_variables[player] for player in self.player_dict) <= self.roster_size
)

# 生成多组阵容
for i in range(self.num_lineups):
    # 对所有已生成的阵容添加重叠限制约束
    for idx, prev_lineup in enumerate(self.previous_lineups):
        # 计算新阵容与旧阵容的重叠球员数:sum(新变量 * 旧变量值)
        overlap = plp.lpSum(
            lp_variables[player] * prev_lineup[player]
            for player in self.player_dict
        )
        # 约束重叠数 ≤ 总人数 - 最小唯一球员数
        self.problem += (
            overlap <= self.roster_size - self.min_unique_players,
            f"MaxOverlap_WithLineup_{idx}"
        )
    
    try:
        self.problem.solve(plp.PULP_CBC_CMD(msg=0))
    except plp.PulpSolverError:
        self.simDoc.update({'jobProgressLog': ArrayUnion(['求解器出错 - 问题不可行。仅生成{}套阵容,目标为{}套。继续导出。'.format(len(self.lineups), self.num_lineups)])})
        break  # 无法生成更多符合要求的阵容,终止循环
       
    score = str(self.problem.objective)
    for v in self.problem.variables():
        score = score.replace(v.name, str(v.varValue))
        
    player_names = [
        v.name.replace("_", " ")
        for v in self.problem.variables()
        if v.varValue != 0
    ]
    fpts = eval(score)

    self.lineups[fpts] = player_names
    
    # 记录当前阵容的球员选择状态,用于后续约束
    current_lineup = {
        player: int(lp_variables[player].varValue)
        for player in self.player_dict
    }
    self.previous_lineups.append(current_lineup)
    
    # 约束后续阵容得分低于当前最优解,生成次优解
    self.problem += plp.lpSum(
        self.player_dict[player]["Fpts"] * lp_variables[player]
        for player in self.player_dict
    ) <= (fpts - 0.001)

关键说明

  1. 状态记录:每次生成阵容后,将每个球员的入选状态(0或1)存入previous_lineups列表,用于后续计算重叠数。
  2. 重叠约束:在生成新阵容前,遍历所有旧阵容,添加约束重叠球员数 ≤ 总人数 - 最小唯一球员数。例如总人数6、最小唯一3人时,重叠最多3人,意味着新阵容至少有6-3=3个球员和旧阵容不同。
  3. 灵活性:min_unique_players参数可自由调整,设为0则关闭该约束,恢复原有逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 21:48:10