【问题标题】:Linear Optimization with Categorical Constraints具有分类约束的线性优化
【发布时间】:2019-08-23 19:06:38
【问题描述】:

我正在尝试根据比赛水平优化球员阵容,基于最大化单个统计数据(在下面的示例中为积分)。对于给定的阵容,有 2 名球员与一个比赛级别进行匹配。这是我的 DataFrame 的一个示例(这只是一个示例,我的实际 DataFrame 的播放器比这多得多):

              Comp Level    Points    Minutes Played    
Player A       Elite         10.1          22
Player A       Middle        -5            22
Player A       Low           7.2           22
Player B       Elite         0.8           20
Player B       Middle        5.6           20
Player B       Low           2.2           20          
Player C       Elite        -7.2           18
Player C       Middle        3.3           21
Player C       Low           6.6           23
Player D       Elite        -7.2           18
Player D       Middle        3.3           21
Player D       Low           6.6           23
Player E       Elite        -7.2           18
Player E       Middle        3.3           21
Player E       Low           6.6           23
Player F       Elite        -7.2           18
Player F       Middle        3.3           21
Player F       Low           6.6           23

优化后的阵容将最大化整个阵容的总分(每个球员的总分)。约束是: 1. 同一个选手不能在多个比赛级别上使用(例如选手A与精英级别配对,则不能与中低级别对战)。 2. 每个阵容对的出场时间值必须在 10% 以内(例如,球员 A 可以与精英级别的球员 B 配对,但不能与球员 C 配对)。

我希望我的输出看起来像这样:

Line up:
Elite: Player A and B
Middle: Player C and Player D
Low: Player E and Player F
Team Total Points: X

我从创建 DataFrame 开始,并考虑使用 PuLP 进行此优化。我正在努力解决的部分是添加我的目标函数和问题的适当约束。

这是我目前的代码:

prob = LpProblem('LineUp Optimization', LpMaximize)
players = list(data['Name'])
# print('The Players under consideration are \n' + '-'*100)
# for x in players:
#     print(x, end=',\n ')

costs = dict(zip(players, data['Points']))
comp_levels = dict(zip(players, data['Competition Level']))
Minutes = dict(zip(players, data['Minutes Played']))
ozs = dict(zip(players, data['OZS%']))

player_var = LpVariable.dicts('Players', players, 0, cat='Integer')

任何关于我的约束在此 PuLP 优化中的样子的帮助/指导将不胜感激!

【问题讨论】:

    标签: python-3.x combinations mathematical-optimization pulp


    【解决方案1】:

    如果我正确理解您的问题,您需要为精英、中级和低级比赛级别选择一对球员。

    您可以通过多种方式来表述这个问题。对我来说最明显的是有一组二进制变量来指示每个玩家是否(1)或不是(0)为每个级别的比赛选择:

    player_var = LpVariable.dicts('player_var', (players, comp_levels), cat='Binary')
    

    您的目标是最大化所包含玩家的总分:

    prob += lpSum([player_var[p, l]*points[p, l] for p in players for l in comp_levels])
    

    然后你需要设置每个级别应该选择两个玩家的约束:

    for l in comp_levels:
        prob += lpSum([player_var[p, l] for p in players]) == 2
    

    并且一名球员最多可以参加一个比赛级别:

    for p in players:
        prob += lpSum([player_var[p, l] for l in comp_levels]) <= 1
    

    并且满足您在所选级别的配对上场时间的 10% 规则。最后一个有点意思。我确信有一种更有效的方法可以做到这一点,但我在下面所做的应该可以工作......我认为。

    for l in comp_levels:
        for p in players:
            prob += minutes[p, l]*player_var[p,l] <= 1.1*lpSum([minutes[p,l]*player_ver[other_p,l] for other_p in players if other_p != p])
    

    这会为每个比赛级别和每个玩家创建一个约束,但是如果没有选择玩家在该级别(player_var[p,l] == 0)进行比赛,那么约束应该没有影响 - 左侧的术语将为 0。

    如果玩家被选中 (player_var[p,l] == 1),则约束确保被选中的玩家的上场时间不超过对手的 10% - 请注意,右手沙子对所有其他玩家求和,但只有其中一位将非零。

    没有进行检查以确保所选球员至少有 90% 的时间在他们的对手身上 - 但是当这个限制应用于他们的对手时,这种情况就会被涵盖。

    我敢肯定,有人可以提出一种更有效/更优雅的方法......

    【讨论】:

    • 感谢您的开始。您完全掌握了我想要做的事情,我认为您的解决方案将完成工作。但是,我正在尝试解决的播放器列表中出现关键错误。我认为抛出错误是因为玩家 A、B、C 等有多个实例(每个比赛级别一个)。这是错误:“文件“Players.py”,第 65 行,在 prob += lpSum([player_var[p, l]*points[p, l] for p in player for l in comp_levels]) KeyError : ('奥利弗·艾克曼-拉尔森', '奥利弗·艾克曼-拉尔森')"
    • 澄清一下.... 我原以为您会生成playerscomp_levels 的列表,其中每个玩家/关卡只有一个实例。 numpy.unique() 应用于您的 df 的玩家名称列应该可以解决问题...
    • 有趣的是,当我使用 np.unique(data['Name'] 时,它会抛出相同的错误;但是,直到遇到第三个玩家时才会抛出该错误。
    • 您的数据是否在 pandas 数据框中?如果是这样,您应该可以做到data.Name.unique()
    • 是的,它在熊猫数据框中。当我使用“data.Name.unique()”时仍然会抛出错误这是我导入 .csv 并设置数据框时代码的开头:“data = pd.read_csv('Lineup(Code Ready) .csv', encoding= 'unicode_escape') data = data[['Name', 'Positions', 'Competition Level', 'EVTOI', 'OZS%', 'DF Multiplier' ]] data.Name.unique() data = data.loc[data['Positions'] == 'D'] "
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-13
    • 1970-01-01
    • 2018-05-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多