简体   繁体   English

如何将 OR 条件纳入纸浆优化问题?

[英]How to incorporate OR condition into pulp optimization problem?

I am trying to build a fantasy team where the goal is to maximize its total expected points based on expected points of individual players subject to certain constraints.我正在尝试建立一个幻想团队,其目标是根据受某些约束的单个玩家的预期分数来最大化其总预期分数。
Here's the relevant part of my code:这是我的代码的相关部分:

players = [player for player in input_dict]
prices = {player: input_dict[player]['price'] for player in players}
player_teams = {player: input_dict[player]['team'] for player in players}
player_positions = {player: input_dict[player]['position'] for player in players}
points = {player: input_dict[player]['expected_points'] for player in players}
league_teams = list(set(team for team in player_teams.values()))

prob = LpProblem('Fantasy_team', LpMaximize)
player_var = LpVariable.dicts('Players', players, 0, 1, cat='Integer')

# maximize points
prob += lpSum([player_var[player] * points[player] for player in players])
# number of players to be selected
prob += lpSum([player_var[player] for player in players]) == 11 
# max budget
prob += lpSum([player_var[player] * prices[player] for player in players]) <= 100
# position constraints
prob += lpSum([player_var[player] for player in players if player_positions[player] == 'goalkeeper']) == 1
prob += lpSum([player_var[player] for player in players if player_positions[player] == 'defender']) >= 2
prob += lpSum([player_var[player] for player in players if player_positions[player] == 'defender']) <= 5
prob += lpSum([player_var[player] for player in players if player_positions[player] == 'midfielder']) >= 2
prob += lpSum([player_var[player] for player in players if player_positions[player] == 'midfielder']) <= 5
prob += lpSum([player_var[player] for player in players if player_positions[player] == 'forward']) >= 0
prob += lpSum([player_var[player] for player in players if player_positions[player] == 'forward']) <= 3

# max players from one team

for league_team in league_teams:
    team_players = []
    for player, lp_var in player_var.items():
        if player_teams[player] == league_team:
            team_players.append(lp_var)
    prob += lpSum(team_players) <= 3

However, what if I want to add one additional constraint: minimum defenders (or any other position) from one team = 2. In other words, at least two defenders have to come from the same team.但是,如果我想添加一个额外的限制条件:一支球队的最少防守队员(或任何其他位置)=2。换句话说,至少两名防守队员必须来自同一支球队。

It seems to me that another decision variable would be required but I do not understand how this should be done.在我看来,需要另一个决策变量,但我不明白应该怎么做。

EDIT:编辑:

I made some progress but I am now stuck with this error.我取得了一些进展,但我现在遇到了这个错误。 Apparently Pulp is not happy with max function.显然 Pulp 对最大 function 不满意。 Is there any way to get around this?有没有办法解决这个问题?

positions = ['defender',]
team_hires = LpVariable.dicts('count', (positions, league_teams), cat='Integer')

for league_team in league_teams:
    player_vars = []
    for player in player_var:
        if player_teams[player] == league_team:
            player_vars.append(player_var[player])
    team_hires['defender'].update({league_team: lpSum(player_vars)})

prob += max([team_hires['defender'][team] for team in league_teams]) >= 2

TypeError: '>' not supported between instances of 'LpAffineExpression' and 'LpAffineExpression'

I needed to re-post this answer as my previous one (deleted) is confusing and I think I led you astray...我需要重新发布这个答案,因为我之前的答案(已删除)令人困惑,我想我把你误入歧途了......

In order to write a constraint that forces at least 1 team to support the minimum number of hires per position, you need to be able to iterate over the teams (so you need a set of teams) and you need to be able to iterate over the positions that have a minimum-hires constraint.为了编写强制至少 1 个团队支持每个 position 的最少雇佣人数的约束,您需要能够迭代团队(因此您需要一组团队)并且您需要能够迭代有最低雇佣限制的职位。 The example below does that with a "multiple choice" constraint that is a binary variable which implies a source team is supporting the min hires for a particular position.下面的示例使用作为二进制变量的“多项选择”约束来实现这一点,这意味着源团队正在支持特定 position 的最低雇佣人数。 So if a particular team is "selected" by the solver to support the min hires, that variable is "1" and then the number of hires from that team must be greater than the min requirement (that is the first part of the constraint).因此,如果求解器“选择”一个特定团队来支持最低雇佣人数,则该变量为“1”,然后该团队的雇佣人数必须大于最低要求(这是约束的第一部分) . The second part is that you must force the sum of that variable over all teams to be at least 1 to ensure that at least one team is forced to support the requirement.第二部分是您必须强制所有团队的该变量的总和至少为 1,以确保至少有一个团队被迫支持该要求。 That is the second part of the constraint.这是约束的第二部分。

Note the syntax on this could probably be a bit cleaner if you changed your decision variable to be triple-indexed [player, pos, source_team], but this still works fine!请注意,如果您将决策变量更改为三索引 [player, pos, source_team],则此语法可能会更简洁,但这仍然可以正常工作!

Data file (data.csv)数据文件 (data.csv)

player,price,team,position,expected_points
bob,24,wolves,defender,2.1
sam,23,wolves,defender,2.4
tommy,20,wolves,forward,3.4
bill,20,wolves,forward,3.6
eddie,22,wolves,forward,3.1
tim,23,bears,defender,2.2
earl,23,bears,defender,1.0
dorf,24,bears,forward,3.5
bennie,30,bears,forward,3.6

Model Model

from pulp import *
import pandas as pd

df = pd.read_csv('data.csv')
df = df.set_index('player')
input_dict = df.to_dict('index')


players = [player for player in input_dict]
prices = {player: input_dict[player]['price'] for player in players}
player_teams = {player: input_dict[player]['team'] for player in players}
player_positions = {player: input_dict[player]['position'] for player in players}
points = {player: input_dict[player]['expected_points'] for player in players}
league_teams = list(set(team for team in player_teams.values()))

# min numbers per position
pos_mins = {'defender':2,
            'forward':2}

# min numbers from single team by position
team_pos_mins = {'defender':2}   # must hire at least 2 defenders from same source team
positions = player_positions.values()
pos_team_combos = {(pos, team) for pos in positions for team in league_teams}

prob = LpProblem('Fantasy_team', LpMaximize)
hire = LpVariable.dicts('Players', players, cat='Binary')   # this is binary decision...
support_min_hires = LpVariable.dicts('team hires', pos_team_combos, cat='Binary')  # 1 if that team supports the min for position


# maximize points
prob += lpSum([hire[player] * points[player] for player in players])
# number of players to be selected
prob += lpSum([hire[player] for player in players]) == 4
# max budget
prob += lpSum([hire[player] * prices[player] for player in players]) <= 100
# position constraints
for pos in pos_mins:
    # this pattern could be replicated for all of your max/mins
    prob += lpSum(hire[player] for player in players if player_positions[player] == pos) >= pos_mins[pos]

# hires by team constraint
for pos in team_pos_mins:
    # the min number hired from team must be greater than the requirement, if that team is selected to support the min...
    for team in league_teams:
        prob += lpSum(hire[player] for player in players 
                if player_positions[player] == pos
                and player_teams[player] == team) >= support_min_hires[pos, team] * team_pos_mins[pos]
    # force at least one team to suppoprt the minimum hires
    prob += lpSum(support_min_hires[pos,team] for team in league_teams) >= 1

#print(prob)

status = prob.solve()
print(status)
for p in players:
    print(p, hire[p].value())

# for QA...
for pos in team_pos_mins:
    for team in league_teams:
        print(f'{team} covers min hires for {pos}:  {support_min_hires[pos,team].value()>0}')

Result结果

Result - Optimal solution found

Objective value:                11.70000000
Enumerated nodes:               0
Total iterations:               0
Time (CPU seconds):             0.00
Time (Wallclock seconds):       0.00

Option for printingOptions changed from normal to all
Total time (CPU seconds):       0.00   (Wallclock seconds):       0.01

1
bob 1.0
sam 1.0
tommy 0.0
bill 1.0
eddie 0.0
tim 0.0
earl 0.0
dorf 0.0
bennie 1.0
wolves covers min hires for defender:  True
bears covers min hires for defender:  False
[Finished in 0.9s]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM