PuLP是一个用于线性规划(LP)、整数线性规划(ILP)和混合整数线性规划(MILP)问题的Python库。PuLP的全称是"Python for Mathematical Programming",它提供了一个简单而强大的工具,使得用户能够定义优化问题、构建数学模型并使用不同的求解器进行求解。PuLP的主要特点之一是其易用性。它允许用户通过简单的方式定义优化问题,而无需深入了解数学规划的细节。PuLP的语法设计旨在使用户能够直观地表达问题的约束和目标函数。这种简洁而清晰的语法使得PuLP成为解决线性规划问题的理想选择,特别是对于那些对数学规划领域不太熟悉的用户。在PuLP中,用户可以轻松地定义变量、约束和目标函数。通过简单的API调用,用户可以指定变量的上下界、约束条件的系数以及目标函数的系数。PuLP还提供了对问题特性的检查和显示,以帮助用户验证模型的正确性。PuLP的使用不仅限于线性规划问题,还可以处理整数线性规划和混合整数线性规划。
一、PuLP库的使用
PuLP Main Topics教程
PuLP是一个开源的第三方工具包,可以求解线性规划、整数规划、混合整数规划问题。下面先介绍一下Pulp一些基础的库函数,再以一些例题为例进行重复讲解。
PuLP库的安装和导入:
pip install pulp
import pulp
import numpy as np
PuLP创建求解问题:
prob = pulp.LpProblem("LPProbDemo", sense=pulp.LpMaximize)
#pulp.LpProblem 是定义问题的构造函数。
#LPProbDemo是用户定义的问题名(用于输出信息)。
#sense用来指定求最小值/最大值问题,可选参数值:LpMinimize、LpMaximize
PuLP定义变量
x1 = pulp.LpVariable('x1',lowBound=0, upBound=None, cat=pulp.LpInteger)
x2 = pulp.LpVariable('x2', lowBound=0, upBound=None, cat=pulp.LpBinary)
x1 = pulp.LpVariable('x1', lowBound=0, upBound=7, cat='Continuous')
#x1,x2是用户定义的变量名
#lowBound、upBound用来设定决策变量的下界、上界;可以不定义下界/上界,默认的下界/上界是负无穷/正无穷。
#cat用来设定变量类型,可选参数值:pulp.LpInteger为离散变量;pulp.LpBinary为0-1变量;pulp.LpInteger为整数变量。
PuLP定义目标函数:
prob += 3*x1 + 5*x2
prob += 2*x0-5*x1+4*x2
PuLP定义约束条件:
prob += 2*x1 + x2 <= 100
prob += x1 + x2 <= 80
prob += x1 <= 40
prob += x2 <= 60
PuLP求解和状态函数:
prob.solve()
pulp.LpStatus[prob.status] #合理Status: Optimal
pulp.value(prob.objective) #目标函数值
二、PuLP库的使用示例
2.1示例1
import pulp
MyProbLP = pulp.LpProblem("LPProbDemo1", sense=pulp.LpMaximize)
x1 = pulp.LpVariable('x1', lowBound=0, upBound=7, cat='Continuous')
x2 = pulp.LpVariable('x2', lowBound=0, upBound=7, cat='Continuous')
x3 = pulp.LpVariable('x3', lowBound=0, upBound=7, cat='Continuous')
MyProbLP += 2*x1 + 3*x2 - 5*x3 # 设置目标函数
MyProbLP += (2*x1 - 5*x2 + x3 >= 10) # 不等式约束
MyProbLP += (x1 + 3*x2 + x3 <= 12) # 不等式约束
MyProbLP += (x1 + x2 + x3 == 7) # 等式约束
MyProbLP.solve()
# 输出求解状态
print("Status:", pulp.LpStatus[MyProbLP.status])
# 输出每个变量的最优值
for v in MyProbLP.variables():
print(v.name, "=", v.varValue)
# 输出最优解的目标函数值
print("F(x) = ", pulp.value(MyProbLP.objective))
# 输出结果如下
Status: Optimal
x1 = 6.4285714
x2 = 0.57142857
x3 = 0.0
F(x) = 14.57142851
2.2示例2
import pulp
import numpy as np
# 定义问题
ProbLP3 = pulp.LpProblem("ProbLP3", sense=pulp.LpMaximize)
# 定义变量
x1 = pulp.LpVariable('x1', lowBound=0, upBound=8, cat='Continuous')
x2 = pulp.LpVariable('x2', lowBound=0, upBound=7.5, cat='Continuous')
# 定义目标函数
ProbLP3 += 11 * x1 + 9 * x2
# 添加约束
ProbLP3 += 6 * x1 + 5 * x2 <= 60
ProbLP3 += 10 * x1 + 20 * x2 <= 150
# 求解问题
ProbLP3.solve()
# 输出结果
print("Problem Name:", ProbLP3.name)
print("Status:", pulp.LpStatus[ProbLP3.status])
# 输出每个变量的最优值
for variable in ProbLP3.variables():
print(f"{variable.name} = {variable.varValue}")
# 输出最优解的目标函数值
print("F3(x) =", pulp.value(ProbLP3.objective))
三、案例——猫粮问题
问题描述:Uncle Ben's希望以尽可能低的成本生产他们的猫粮产品,同时确保它们符合罐头上显示的营养分析要求。猫粮的配料有chicken, beef, mutton,rice, wheat,gel,它们的成本分别是$0.013, $0.008,$0.010,$0.002, $0.005, $0.001。为了满足营养标准,每100g成品必须至少有8gProtein,6gfat,但是不超过2g的fibre以及0.4g的salt。下面是营养成分表。
Stuff | Protein | Fat | Fibre | Salt |
---|---|---|---|---|
Chicken | 0.100 | 0.080 | 0.001 | 0.002 |
Beef | 0.200 | 0.100 | 0.005 | 0.005 |
Mutton | 0.150 | 0.110 | 0.003 | 0.007 |
Rice | 0.000 | 0.010 | 0.100 | 0.002 |
Wheat bran | 0.040 | 0.010 | 0.150 | 0.008 |
Gel | 0.000 | 0.000 | 0.000 | 0.000 |
3.1 建模表示
为使得更改其他测试的任何问题数据变得更容易,我们将以代数方式定义问题的方式开始:
确定决策变量
决策变量是我们在罐中包含的不同成分的百分比。由于罐子重100g,这些百分比也代表每种成分包含的克数。请注意,这些百分比必须介于0和100之间。
制定目标函数
对于Whiskas猫食品问题,目标是最小化每罐猫食品成分的总成本,即
确定约束条件
Whiskas猫食品问题的约束条件是:
- 百分比总和必须占整个罐子(= 100%)。
- 满足规定的营养分析要求。
“整罐”的约束是:
为了满足营养分析要求,我们需要每100g至少8g蛋白质,6g脂肪,但不超过2g纤维和0.4g盐。为了制定这些约束条件,我们利用以前的每种成分贡献表。这使我们能够制定有关来自成分的蛋白质,脂肪,纤维和盐总贡献的以下约束条件:
\[ \begin{aligned} 0.100x_1&+0.200x_2+0.150x_3+0.000x_4+0.040x_5+0.0x_6≥8.0\\ 0.080x_1&+0.100x_2+0.110x_3+0.010x_4+0.010x_5+0.0x_6≥6.0\\ 0.001x_1&+0.005x_2+0.003x_3+0.100x_4+0.150x_5+0.0x_6≤2.0\\ 0.002x_1&+0.005x_2+0.007x_3+0.002x_4+0.008x_5+0.0x_6≤0.4 \end{aligned} \]3.2 python实现
import numpy as np
from pulp import *
#Creates a list of the Ingredients
Ingredients = ['CHICKEN', 'BEEF', 'MUTTON', 'RICE', 'WHEAT', 'GEL']
# A dictionary of the costs of each of the Ingredients is created
costs = {'CHICKEN': 0.013,
'BEEF': 0.008,
'MUTTON': 0.010,
'RICE': 0.002,
'WHEAT': 0.005,
'GEL': 0.001}
# A dictionary of the protein percent in each of the Ingredients is created
proteinPercent = {'CHICKEN': 0.100,
'BEEF': 0.200,
'MUTTON': 0.150,
'RICE': 0.000,
'WHEAT': 0.040,
'GEL': 0.000}
# A dictionary of the fat percent in each of the Ingredients is created
fatPercent = {'CHICKEN': 0.080,
'BEEF': 0.100,
'MUTTON': 0.110,
'RICE': 0.010,
'WHEAT': 0.010,
'GEL': 0.000}
# A dictionary of the fibre percent in each of the Ingredients is created
fibrePercent = {'CHICKEN': 0.001,
'BEEF': 0.005,
'MUTTON': 0.003,
'RICE': 0.100,
'WHEAT': 0.150,
'GEL': 0.000}
# A dictionary of the salt percent in each of the Ingredients is created
saltPercent = {'CHICKEN': 0.002,
'BEEF': 0.005,
'MUTTON': 0.007,
'RICE': 0.002,
'WHEAT': 0.008,
'GEL': 0.000}
# 创建问题实例,求最小极值
prob = LpProblem("The Whiskas Problem", LpMinimize)
# 构建Lp变量字典,键名是Ingredients,值(变量名)以Ingr开头,如Ingr_CHICKEN,下界是0
ingredient_vars = LpVariable.dicts("Ingr", Ingredients, 0)
# 添加目标方程
prob += lpSum([costs[i] * ingredient_vars[i] for i in Ingredients])
# 添加约束条件
prob += lpSum([ingredient_vars[i] for i in Ingredients]) == 100
prob += lpSum([proteinPercent[i] * ingredient_vars[i] for i in Ingredients]) >= 8.0
prob += lpSum([fatPercent[i] * ingredient_vars[i] for i in Ingredients]) >= 6.0
prob += lpSum([fibrePercent[i] * ingredient_vars[i] for i in Ingredients]) <= 2.0
prob += lpSum([saltPercent[i] * ingredient_vars[i] for i in Ingredients]) <= 0.4
# 求解
prob.solve()
# 查看解的状态
print("Status:", LpStatus[prob.status])
# 查看解
for v in prob.variables():
print(v.name, "=", v.varValue)
3.3 计算结果
Status: Optimal
Ingr_BEEF = 60.0
Ingr_CHICKEN = 0.0
Ingr_GEL = 40.0
Ingr_MUTTON = 0.0
Ingr_RICE = 0.0
Ingr_WHEAT = 0.0