多位置球员导致的阵容优化器实现问题求助
多位置球员导致的阵容优化器实现问题求助
我完全懂你的痛点——多位置球员的阵容筛选确实比单一位置的麻烦太多了,毕竟不能直接硬套位置匹配规则,还要避免重复选同一个人,同时得盯着总投影最高的组合来选。我给你梳理几个可行的解决思路,从简单暴力到高效建模都有,你可以根据自己的球员数量来挑:
首先,先把你的原始数据转成结构化的格式,方便后续处理,比如用字典列表存储每个球员的信息:
# 先解析你的原始数据成结构化列表 raw_data = """juan-soto 30.3773 ['OF'] kyle-tucker 44.0626 ['OF'] yordan-alvarez 32.510200000000005 ['CI', 'OF'] william-contreras 26.7904 ['CI', 'MI']""" players = [] for line in raw_data.splitlines(): name, proj_str, pos_str = line.split(maxsplit=2) proj = float(proj_str) # 把位置字符串转成列表,比如['CI', 'OF']处理成['CI', 'OF'] positions = pos_str.strip('[]').replace("'", "").split(', ') players.append({ "name": name, "projection": proj, "positions": positions })
方法一:暴力组合+验证(适合球员少的情况,比如少于20人)
如果你的球员列表不多,直接穷举所有5人组合,然后验证每个组合是否能满足位置要求,最后选总投影最高的就行。
核心是写一个验证函数,判断给定的5人组合是否存在一种位置分配方式,满足1个CI、1个MI、1个OF,剩下2个当FLEX。这里用递归回溯的方式尝试所有可能的分配,确保不会漏掉任何潜在的有效组合:
def can_assign(remaining_players, needed_positions): # 递归终止条件:所有需求都满足了 if all(count == 0 for count in needed_positions.values()): return True # 遍历当前还需要的位置 for pos, required in needed_positions.items(): if required <= 0: continue # 找所有能打这个位置且还没被分配的球员 eligible = [p for p in remaining_players if pos in p["positions"]] if not eligible: continue # 尝试用其中一个球员来满足这个位置需求 for player in eligible: # 复制需求字典,避免修改原数据 new_needed = needed_positions.copy() new_needed[pos] -= 1 # 复制球员列表,移除当前选中的球员(不能重复用) new_players = remaining_players[:] new_players.remove(player) # 递归验证剩下的需求和球员 if can_assign(new_players, new_needed): return True return False def is_valid_lineup(combo): # 基础需求:1个CI、1个MI、1个OF,剩下2个FLEX不需要额外限制 base_needs = {"CI": 1, "MI": 1, "OF": 1} return can_assign(list(combo), base_needs)
然后就可以遍历所有5人组合,筛选有效组合并找出最优:
from itertools import combinations def find_best_bruteforce(players): best_total = -1.0 best_lineup = None for combo in combinations(players, 5): if is_valid_lineup(combo): total = sum(p["projection"] for p in combo) if total > best_total: best_total = total best_lineup = combo return best_lineup, best_total # 调用测试 lineup, total = find_best_bruteforce(players) if lineup: print(f"最佳总投影: {total:.4f}") print("阵容:") for p in lineup: print(f"- {p['name']} | 投影: {p['projection']:.4f} | 位置: {p['positions']}") else: print("没有符合要求的阵容")
方法二:整数规划(适合球员多的情况,高效不暴力)
如果你的球员数量很多(比如20+),暴力穷举组合会非常慢,这时候可以用线性规划库PuLP来建模求解,效率会高很多。
这个思路是把问题转化为数学规划:每个球员要么选要么不选,目标是总投影最大,约束条件是:
- 总共选5个球员
- 选中的球员里,至少有1个能打CI
- 至少有1个能打MI
- 至少有1个能打OF
代码实现如下:
from pulp import LpProblem, LpVariable, LpMaximize, lpSum # 创建最大化问题 prob = LpProblem("OptimalFantasyLineup", LpMaximize) # 给每个球员创建0-1变量:1表示选中,0表示不选 player_vars = LpVariable.dicts( "Player", [p["name"] for p in players], lowBound=0, upBound=1, cat="Integer" ) # 目标函数:总投影最大化 prob += lpSum([p["projection"] * player_vars[p["name"]] for p in players]) # 约束1:必须选恰好5个球员 prob += lpSum([player_vars[p["name"]] for p in players]) == 5 # 约束2:至少有1个能打CI的球员被选中 prob += lpSum([player_vars[p["name"]] for p in players if "CI" in p["positions"]]) >= 1 # 约束3:至少有1个能打MI的球员被选中 prob += lpSum([player_vars[p["name"]] for p in players if "MI" in p["positions"]]) >= 1 # 约束4:至少有1个能打OF的球员被选中 prob += lpSum([player_vars[p["name"]] for p in players if "OF" in p["positions"]]) >= 1 # 求解问题(PuLP会自动调用合适的求解器) prob.solve() # 输出结果 print("最佳阵容(整数规划解法):") total_proj = 0.0 for p in players: if player_vars[p["name"]].varValue == 1: print(f"- {p['name']} | 投影: {p['projection']:.4f} | 位置: {p['positions']}") total_proj += p["projection"] print(f"总投影: {total_proj:.4f}")
这个方法不需要穷举所有组合,求解器会直接找到最优解,适合大数量的球员列表。
补充说明
这里的关键是不要纠结球员的“所有位置”,而是关注“是否存在至少一种位置分配方式满足阵容需求”——只要一个球员能覆盖某个位置需求,并且整个组合能通过分配不同球员到对应位置来填满所有配额,同时不重复使用球员,这个组合就是有效的。
备注:内容来源于stack exchange,提问作者markus
相关产品推荐
相关产品推荐

