线性规划——Pyhton线性规划求解库PULP的使用

发布时间 2023-11-27 16:35:09作者: 郝hai

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 += 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

# 导入 PuLP库函数
import pulp
# 定义一个规划问题
MyProbLP = pulp.LpProblem("LPProbDemo1", sense=pulp.LpMaximize)
'''
pulp.LpProblem 是定义问题的构造函数。
  "LPProbDemo1"是用户定义的问题名(用于输出信息)。
   sense 用来指定求最小值/最大值问题,可选参数值:LpMinimize、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') 
'''
pulp.LpVariable 是定义决策变量的函数。
  ‘x1’ 是用户定义的变量名。
  参数 lowBound、upBound 用来设定决策变量的下界、上界;可以不定义下界/上界,默认的下界/上界是负无穷/正无穷。本例中 x1,x2,x3 的取值区间为 [0,7]。
  参数 cat 用来设定变量类型,可选参数值:
  ‘Continuous’ 表示连续变量(默认值)、
  ’Integer ’ 表示离散变量(用于整数规划问题)、
  ’Binary ’ 表示0/1变量(用于0/1规划问题)。
'''
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))  
# 输出结果如下
'''
Optimal - objective value 14.571429
Optimal objective 14.57142857 - 2 iterations time 0.002
Option for printingOptions changed from normal to all
Total time (CPU seconds):   0.00   (Wallclock seconds):  0.00

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之间。

\[x_1=一罐猫粮中所用鸡肉的百分比 \quad x_2=一罐猫粮中所用牛肉的百分比\\ x_3=一罐猫粮中所用羊肉的百分比\quad x_4=一罐猫粮中所用米饭的百分比\\ x_5=一罐猫粮中所用麦麸的百分比\quad x_6=一罐猫粮中所用凝胶的百分比 \]

制定目标函数
对于Whiskas猫食品问题,目标是最小化每罐猫食品成分的总成本,即

\[\min \quad 0.013x_1+0.008x_2+0.010x_3+0.002x_4+0.005x_5+0.001x_6 \]

确定约束条件
Whiskas猫食品问题的约束条件是:
- 百分比总和必须占整个罐子(= 100%)。
- 满足规定的营养分析要求。
“整罐”的约束是:

\[x_1+x_2+x_3+x_4+x_5+x_6=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

参考文献

  1. 整数线性规划——pulp指南
  2. 优化模型(一)
  3. 使用python做线性规划求解