【问题标题】:Python PULP - Positional Constraints for a Lineup GeneratorPython PULP - 阵容生成器的位置约束
【发布时间】:2021-02-23 02:40:28
【问题描述】:

我已经构建了一个 NBA 阵容生成器,可以优化 9 个位置的阵容(2 x 控球后卫 (PG)、2 ​​x 得分后卫 (SG)、2 ​​x 小前锋 (SF)、2 x 大前锋 (PF) , 1 x Center (C)) 使用为每个玩家提供的投影。

最初为了处理可以打两个位置的球员,我设置了一个限制,阻止同一球员在一个阵容中被选中两次,并且它工作正常。我遇到的问题是,当我构建多个阵容时,其中两个偶尔会重复,但程序认为它们是不同的。

生成器版本 1

例如,会出现一个阵容,其中球员 A 和球员 B 都被选中,但他们都是双位置球员,并且他们共享相同的双位置(例如,两者都可以是 PG 或 SG),对于生成器将它们互换,因为在阵容 1 中,球员 A 被选为 PG,B 作为 SG,而在阵容 2 中,球员 A 被选为 SG,B 被选为 PG。

我在第一个版本中限制位置的代码如下:

    prob += (pulp.lpSum(self.positions['PG'][i] * players_lineup[i] for i in range(self.num_players)) == 2)
    prob += (pulp.lpSum(self.positions['SG'][i] * players_lineup[i] for i in range(self.num_players)) == 2)
    prob += (pulp.lpSum(self.positions['SF'][i] * players_lineup[i] for i in range(self.num_players)) == 2)
    prob += (pulp.lpSum(self.positions['PF'][i] * players_lineup[i] for i in range(self.num_players)) == 2)
    prob += (pulp.lpSum(self.positions['C'][i] * players_lineup[i] for i in range(self.num_players)) == 1)

因为程序将玩家 A 的 PG 版本和玩家 A 的 SG 版本视为独立的实体,所以它实际上改变了结果(尽管功能相同)。

生成器的第 2 版

所以我创建了一个更新的版本,其中每个玩家都有一个位置 1 和一个位置 2(如果玩家没有第二位置,则可以没有)。我在这里遇到的问题是我现在可以生成一个满足以下示例中的约束的阵容,但该阵容在技术上是不正确的。首先,我将提供我的新约束,然后我将解释我的问题。

    #Ensures that the lineup has at least 2 potential suitors for PG
    prob += (pulp.lpSum(self.positions['PG'][i] * players_lineup[i] for i in range(self.num_players)) >= 2)
    # Ensures that the lineup has no more than 2 players who can only play PG
    prob += (pulp.lpSum(
        self.positions['PG'][i] * self.dualPosition[i] * players_lineup[i] for i in range(self.num_players)) <=2)
    #Ensures that the lineup has at least 2 potential suitors for SG
    prob += (pulp.lpSum(self.positions['SG'][i] * players_lineup[i] for i in range(self.num_players)) >= 2)
    # Ensures that the lineup has no more than 2 players who can only play SG
    prob += (pulp.lpSum(
        self.positions['SG'][i] * self.dualPosition[i] * players_lineup[i] for i in range(self.num_players)) <= 2)
    #Ensures that the lineup has at least 2 potential suitors for SF
    prob += (pulp.lpSum(self.positions['SF'][i] * players_lineup[i] for i in range(self.num_players)) >= 2)
    # Ensures that the lineup has no more than 2 players who can only play SF
    prob += (pulp.lpSum(
        self.positions['SF'][i] * self.dualPosition[i] * players_lineup[i] for i in range(self.num_players)) <= 2)
    #Ensures that the lineup has at least 2 potential suitors for PF
    prob += (pulp.lpSum(self.positions['PF'][i] * players_lineup[i] for i in range(self.num_players)) >= 2)
    # Ensures that the lineup has no more than 2 players who can only play PF
    prob += (pulp.lpSum(
        self.positions['PF'][i] * self.dualPosition[i] * players_lineup[i] for i in range(self.num_players)) <= 2)
    #Ensures that the lineup has at least 1 potential suitor for C
    prob += (pulp.lpSum(self.positions['C'][i] * players_lineup[i] for i in range(self.num_players)) >= 1)
    # Ensures that the lineup has no more than 1 player who can only play C
    prob += (pulp.lpSum(
        self.positions['C'][i] * self.dualPosition[i] * players_lineup[i] for i in range(self.num_players)) <= 1)

我在这里面临的问题是约束得到满足,但它们显然没有达到我想要的效果哈哈!我发现正在构建如下阵容:

Player Pos 1 Pos 2
A PG None
B PG None
C PG SG
D PG SG
E SG SF
F SF PF
G SF PF
H C None
I PG SG

基于以上可用 PG - 5、SG - 4、SF - 3、PF - 2、C - 1 的总数都符合我的最低标准。问题是 3 个玩家弥补了所有 SF 和 PF 插槽的可用性,因此如果我将 2 个 PF 选项放入 2 个 PF 插槽,我只剩下 1 个 SF 用于 2 个 SF 插槽。

我不知道如何更新我的限制以确保所有阵容位置都有保证供应。

对冗长的文章表示歉意,如果有什么我可以澄清的,请告诉我。

【问题讨论】:

    标签: python linear-programming pulp


    【解决方案1】:

    我认为,如果你重新表述你的问题,你会更快乐。这里的整数程序很自然地适合用一组玩家和一组位置来对它进行双重索引。在您的约束等方面,事情会变得更加清晰,因为您可以对玩家或位置进行总结,以更清楚地表述您的问题。真的不清楚您的许多模型项目类似于positions[i]dualPositions[i]。这是一个解决问题的玩具模型,可能会帮助您考虑对这个坏男孩进行双重索引...

    请注意,有几种方法可以处理其中的“法律分配”部分。以下是如何做到这一点的一个概念。

    # hoops dream team
    
    from pulp import *
    
    salaries = {'bob'   : 100,
                'steve' : 110,
                'bernie': 105,
                'eugene': 120,
                'biff'  : 115, 
                'reggie': 99,
                'mike'  : 102}
    
    positions = {'PG','FWD', 'CTR'}
    
    legal_assignments = {   'bob'   : {'PG'},
                            'steve' : {'PG', 'FWD'},
                            'bernie': {'FWD', 'CTR'},
                            'eugene': {'FWD', 'PG'},
                            'biff'  : {'PG'},
                            'reggie': {'CTR', 'FWD'},
                            'mike'  : {'FWD'}}
    
    legal_assignments_set = { (k,v) for k in legal_assignments.keys() for v in legal_assignments[k]}
    
    prob = LpProblem("Team", LpMinimize)
    
    assign = LpVariable.dicts("assignment", legal_assignments_set, cat="Binary")
    
    #minimize cost of team
    prob += sum(assign[player, pos] * salaries[player] for (player, pos) in legal_assignments_set)
    
    # constraints...
    
    # only assign each player once
    for player in salaries.keys():
        prob += sum(assign[player, pos] for pos in positions if (player, pos) in legal_assignments_set) <=1
    
    # fill each position to 2 PG, 2 FWD, 1 CTR
    prob += sum(assign[player, 'PG'] for player in salaries.keys() if (player, 'PG') in legal_assignments_set) == 2
    prob += sum(assign[player, 'FWD'] for player in salaries.keys() if (player, 'FWD') in legal_assignments_set) == 2
    prob += sum(assign[player, 'CTR'] for player in salaries.keys() if (player, 'CTR') in legal_assignments_set) == 1
    
    prob.solve()
    print('Status:', LpStatus[prob.status])
    for v in prob.variables():
        print(v.name, '=', v.varValue)
    

    【讨论】:

    • 感谢 AirSquid!我会玩一会,看看我的表现如何,感谢快速响应和有用的提示
    • 你说的对,这完全符合我的预期,就像你说的那样!我现在很高兴,因为我重新提出了问题,非常感谢您的建议
    • @diongat 那么你最终的工作代码是什么?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-05
    相关资源
    最近更新 更多