NSDT工具推荐Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/GLB在线编辑 - 3D模型格式在线转换 - 可编程3D场景编辑器 - REVIT导出3D模型插件 - 3D模型语义搜索引擎 - Three.js虚拟轴心开发包

线性规划(或线性优化)是在有约束的数学问题中求解最佳结果的过程。PuLP是一个强大的库,它可以帮助 Python 用户只用几行代码解决这些类型的问题。

我发现 PuLP 是解决这些类型的线性优化问题的最简单的库。目标函数和约束都可以通过一种有趣的分层方法添加,每个方法只需一行代码。这意味着我们可以花更少的时间编码和更多的时间来解决问题。该文档也易于阅读,包括五个易于遵循的案例研究

在本文中,我们将使用来自Fanduel的每日梦幻体育 (DFS) 数据来演示如何解决具有多个约束的最大化问题。目的是这些步骤可以推广到你想要解决的其他问题。

我们将使用 DFS 数据,因为它允许我们完成从理解现实问题到根据目标函数和约束定义问题,再到最终用 Python 编写解决方案的整个过程。所有这些步骤都是任何线性规划问题的重要组成部分。DFS 是一个足够简单的上下文来理解这些步骤,同时仍然足够复杂以允许讨论它们。

如果您想继续,可以按照以下步骤免费获得数据:

  1. 在Fanduel创建一个免费的DFS帐户
  2. 前往大厅内的 NBA 选项卡
  3. 单击下面的任何比赛,然后单击“进入新阵容”按钮
  4. 最后,点击页面顶部的“下载选手列表”,获取数据为csv文件

2、NBA中关于DFS的背景

在我们进入文章之前,我们将快速了解 Fanduel 为 NBA 组织比赛的方式。

这一背景将为我们如何为我们试图解决的问题设置约束奠定基础。在坐下来实际编写代码之前,总是有必要了解线性规划中的问题。这有助于我们在坐下来编写代码时形成约束和目标函数。

目标是建立一个由 9 名得分最高的球员组成的阵容。球员通过在当天的比赛中做一些成功的事情来获得积分,比如得分或抢到篮板,而因为失误等负面行为而丢分。每个篮球运动员在那天都会得到一份假想的薪水(来自 Fandeul),你会得到 60,000 美元来分配给这些球员。你必须选择2个控球后卫、2个得分后卫、2个小前锋、2个大前锋和1个中锋。

3、我们要解决的问题

现在我们已经很好地理解了我们要解决的问题,让我们用目标函数正式定义它:

  • 最大化我们 9 名玩家的预测分数。

问题的约束是:

  1. 最多只能购买一名球员1次。
  2. 拥有2个控球后卫、2个得分后卫、2个小前锋、2个大前锋和1个中锋。
  3. 花费不超过 60,000 美元。

4、使用PuLP解决这个问题

我们现在可以开始实际编写代码来解决这个问题。由于这是一篇关于优化的文章(而不是关于预测结果的文章),我们将使用每个选手的平均得分作为他们今天的预测得分。如果我们要为 Fanduel 构建一个真正的优化器,我们会希望改进我们的估计,以包括其他变量,例如对战和每位球员的预计上场时间。

5、PuLP初始设置

首先,让我们在命令行中使用 pip 安装软件包:

pip install pulp

并在我们的 Jupyter notebook 或 IDE 中导入必要的包:

from pulp import *
import pandas as pd

然后,我们将使用pd.read_csv() 读取数据,得到一个 pandas DataFrame,其中包括Nickname(Fanduel 上的选手姓名)、FPPG(该选手每场比赛得分的平均数)Salary以及Position这些我们将调用的变量。

6、设置PuPL数据结构

我们现在需要使用字典来定义变量,因为这些是PuLP使用的数据结构:

# Get a list of players
players = list(data['Nickname'])
# Initialize Dictionaries for Salaries and Positions
salaries = dict(zip(players, data['Salary']))
positions = dict(zip(players, data['Position']))
# Dictionary for Projected Score for each player
project_points = dict(zip(players, data['FPPG']))
# Set Players to Take either 1 or 0 values (owned or not)
player_vars = LpVariable.dicts("Player", players, lowBound=0, upBound=1, cat='Integer')

除了最后几行之外,所有内容都设置了字典,将存储在Nickname 中的选手姓名指向我们感兴趣的其他变量。

最后一行使用LpVariables,定义与第二个参数(在本例中players)数值相关的变量。其他参数定义了player_vars可以采用的值。参数cat可以设置为'Integer''Continuous'。我们得到一个字典,选手的名字映射到整数(我们将使用它来指示我们是否拥有玩家,其值分别为 1 或 0)。

7、在PuPL中定义问题

接下来,我们需要使用LpProblem()方法设置问题:

total_score = LpProblem("Fantasy_Points_Problem", LpMaximize)

第一个参数是问题的名称,第二个参数sense可以设置为LpMinimizeLpMaximize。我们使用LpMaximize,因为我们试图最大化目标点。

定义问题后,我们使用lpsum()方法添加目标函数:

total_score += lpSum([project_points[i] * player_vars[i] for i in player_vars])

我们的工资限制:

total_score += lpSum([salaries[i] * player_vars[i] for i in player_vars]) <= 60000

和我们的位置限制:

# Get indices of players for each position
pg = [p for p in positions.keys() if positions[p] == 'PG']
sg = [p for p in positions.keys() if positions[p] == 'SG']
sf = [p for p in positions.keys() if positions[p] == 'SF']
pf = [p for p in positions.keys() if positions[p] == 'PF']
c = [p for p in positions.keys() if positions[p] == 'C']
# Set Constraints
total_score += lpSum([player_vars[i] for i in pg]) == 2
total_score += lpSum([player_vars[i] for i in sg]) == 2
total_score += lpSum([player_vars[i] for i in sf]) == 2
total_score += lpSum([player_vars[i] for i in pf]) == 2
total_score += lpSum([player_vars[i] for i in c]) == 1

8、在PuLP中问题求解

一旦我们定义了问题,我们就可以用一行代码解决问题!

total_score.solve()

完成此操作后,我们的优化变量通过total_score.variables()调用存储在列表中,每个xuanshou 的值存储在变量varValue中,值的名称存储在每个变量的name变量中。因此,可以通过查找具有非零值的球员来打印我们的阵容,如下所示:

for v in total_score.variables():
    if v.varValue > 0:
        print(v.name)

9、结论

我们现在能够在 Python 中使用 PuLP 解决复杂的线性规划问题!一旦我们了解了我们要解决的问题,我们就可以使用这个库在几行代码中解决它。

线性优化是许多领域的重要组成部分,例如运营、物流、资本配置等。学习此类技能的最佳方法是自己解决问题。你可以使用我们在上面完成的相同步骤:

  1. 了解问题
  2. 根据目标函数和约束定义问题
  3. 使用 PuLP 解决问题

原文链接:How to Solve Optimization Problems with Python

BimAnt翻译整理,转载请标明出处