发布于2026-07-06 阅读(0)
扫一扫,手机访问
本文介绍如何在Gurobi混合整数规划模型中,通过数学约束显式排除已生成的可行解(如NBA DFS阵容),避免使用Solution Pool机制,实现多解去重。核心思路是:将历史解编码为线性不等式约束,确保新解与所有已有解至少在一个变量上不同。

在Gurobi中,不能直接在约束里使用Python运行时的对象(比如set()、list()或者 .X 取值)来参与建模——因为 .X 是求解之后才生效的属性,建模阶段模型还没求解,y[x].X 一调用就会报 AttributeError;另外 set() 这种结构既不可哈希,Gurobi也没法解析成符号表达式。你可能会写出这样的代码:
m.addConstrs((set([x for x in player_pos_map if y[x].X == 1]) != set(i) for i in created_lineups), name='unique_lineup')
这句话乍一看挺像那么回事,但仔细一推敲,里面埋了两个大坑:
✅ 正确的做法是:对每个已生成的阵容 i ∈ created_lineups,添加一个“汉明距离至少为1”的线性约束,强制当前解 y 与 i 至少在一个变量上取值不同。
假设 player_pos_map 是球员-位置二元变量字典(比如 y[('LeBron', 'SF')]),而 created_lineups 是若干已知阵容的集合,每个阵容可以表示为 {('LeBron','SF'), ('Curry','PG'), ...} 或对应的0-1向量。下面给出标准实现:
对于每个已存在的阵容 S ∈ created_lineups,添加一条约束:
for idx, S in enumerate(created_lineups):
# S 是一个 frozenset 或 tuple of selected (player, pos) keys
# 这个约束的意思是:当前解中选中的变量之和 ≤ |S| - 1
# 换句话说,不能全部命中 S 中的 |S| 个变量——必须至少漏掉一个
m.addConstr(
quicksum(y[key] for key in y.keys() if key in S) <= len(S) - 1,
name=f'no_repeat_{idx}'
)
原理其实很简单:如果当前解恰好等于 S,那么左侧求和就等于 |S|,这时候 ≤ |S|−1 就违反了;但只要有一个变量不同——比如某个球员没入选或者换了个位置——和值就小于等于 |S|−1,约束自动满足。这就是经典的“no-good cut”(不可行解切割)技术。
created_lineups = []
for sol_idx in range(5): # 生成5个不同的最优解
m.optimize()
if m.status != GRB.OPTIMAL:
break
# 提取当前整数解(注意:前提是y为整数变量)
current_lineup = tuple(sorted(key for key in y.keys() if abs(y[key].X - 1) < 1e-6))
# 防止重复加入(可选,增加一点鲁棒性)
if current_lineup not in created_lineups:
created_lineups.append(current_lineup)
print(f"Solution {sol_idx + 1}: {current_lineup}")
# 关键一步:添加排除约束
m.addConstr(
quicksum(y[key] for key in y.keys() if key in current_lineup) <= len(current_lineup) - 1,
name=f'exclude_sol_{sol_idx}'
)
把离散的“解唯一性”转化成线性不等式,既能绕过 PoolMode 那种黑盒行为,又能跟现有的业务约束(比如球员出场频率限制)无缝整合,实现可控、可解释的多解生成。这才是真正把优化控制在手里的做法。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8