在之前的例子中,我们是用LpVariable
对每个模型变量分别给定其名称、类型和上下界,但是在比较大规模的问题中,我们是很难这样一个一个去为变量进行定义的。因此我们需要一种更快捷的方式,能够结合编程语言的循环和容器,方便的创建问题。
问题描述
这里借用《运筹学》第二版中的瑞典钢材生产问题作为例子,学习如何从字典创建模型。
原问题简单转述如下(单位略有修改):
瑞典一家钢铁厂想要通过熔化金属废料再混入添加剂的方式生产满足各种化学成分要求的合金产品。该钢铁厂计划生产1000千克的熔炉进料。所有钢材的主要原料是铁。下表列举了各类材料的微量元素含量占比、可用数量和单位成本。另外成品中的元素占比要求也列在表的最后两行:
碳占比(%) | 镍占比(%) | 铬占比(%) | 钼占比(%) | 可用量(千克) | 成本(元/千克) | |
---|---|---|---|---|---|---|
废料1 | 0.80 | 18 | 12 | 0 | 75 | 16 |
废料2 | 0.70 | 3.2 | 1.1 | 0.1 | 250 | 10 |
废料3 | 0.85 | 0 | 0 | 0 | 无限制 | 8 |
废料4 | 0.40 | 0 | 0 | 0 | 无限制 | 9 |
镍 | 100 | 无限制 | 48 | |||
铬 | 100 | 60 | ||||
钼 | 100 | 53 | ||||
成品中最高占比 | 0.65 | 3.0 | 1.0 | 1.1 | ||
成品中最低占比 | 0.75 | 3.5 | 1.2 | 1.3 |
决策变量
在这个问题中,我们可以将各材料的质量作为决策变量:
其中代表4种废料,
代表3种添加剂。
目标函数
这个问题中目标函数很明显时使成本最小化,可以用决策变量表达为:
约束条件
质量约束:
成分约束:
变量上下界:
编程求解
对于以上的过程,我们利用PULP库中提供的函数进行编程求解。基本的求解套路和上一篇差不多,只是这一次在设置约束和目标函数时,不再直接手打方程,而是用lpSum
和列表推导式进行表达。
from pulp import *
# 1. 建立问题
model = LpProblem("钢材生产问题", LpMinimize)
# 2. 建立变量
ingredients = ['废料1', '废料2', '废料3', '废料4', '镍', '铬', '钼']
ingMass = LpVariable.dicts("Ingr", ingredients, lowBound = 0, cat=LpContinuous)
# 3. 设置目标函数
costs = {
'废料1': 16,
'废料2': 10,
'废料3': 8,
'废料4': 9,
'镍': 48,
'铬': 60,
'钼': 53
}
model += lpSum([costs[item] * ingMass[item] for item in ingredients]), "总生产成本"
# 4. 施加约束
carbonPercent = {
'废料1': 0.008,
'废料2': 0.007,
'废料3': 0.0085,
'废料4': 0.004,
'镍': 0,
'铬': 0,
'钼': 0
}
NiPercent = {
'废料1': 0.18,
'废料2': 0.032,
'废料3': 0,
'废料4': 0,
'镍': 1,
'铬': 0,
'钼': 0
}
CrPercent = {
'废料1': 0.12,
'废料2': 0.011,
'废料3': 0,
'废料4': 0,
'镍': 0,
'铬': 1,
'钼': 0
}
MoPercent = {
'废料1': 0,
'废料2': 0.001,
'废料3': 0,
'废料4': 0,
'镍': 0,
'铬': 0,
'钼': 1
}
model += lpSum([ingMass[item] for item in ingredients]) == 1000, "质量约束"
model += lpSum([carbonPercent[item]*ingMass[item] for item in ingredients]) >= 6.5, "碳最小占比"
model += lpSum([carbonPercent[item]*ingMass[item] for item in ingredients]) <= 7.5, "碳最大占比"
model += lpSum([NiPercent[item]*ingMass[item] for item in ingredients]) >= 30, "镍最小占比"
model += lpSum([NiPercent[item]*ingMass[item] for item in ingredients]) <= 35, "镍最大占比"
model += lpSum([CrPercent[item]*ingMass[item] for item in ingredients]) >= 10, "铬最小占比"
model += lpSum([CrPercent[item]*ingMass[item] for item in ingredients]) <= 12, "铬最大占比"
model += lpSum([MoPercent[item]*ingMass[item] for item in ingredients]) >= 11, "钼最小占比"
model += lpSum([MoPercent[item]*ingMass[item] for item in ingredients]) <= 13, "钼最大占比"
model += ingMass['废料1'] <= 75, "废料1可用量"
model += ingMass['废料2'] <= 250, "废料2可用量"
# 5. 求解
model.solve()
# 6. 打印结果
print(model)
print("求解状态:", LpStatus[model.status])
for v in model.variables():
print(v.name, "=", v.varValue)
print("最优总成本 = ", value(model.objective))
这里特别需要注意的是各个字典中,key应当与建立变量时列表中的元素一一对应。
输出的结果如下:
钢材生产问题:
MINIMIZE
16*Ingr_废料1 + 10*Ingr_废料2 + 8*Ingr_废料3 + 9*Ingr_废料4 + 53*Ingr_钼 + 60*Ingr_铬 + 48*Ingr_镍 + 0
SUBJECT TO
质量约束: Ingr_废料1 + Ingr_废料2 + Ingr_废料3 + Ingr_废料4 + Ingr_钼 + Ingr_铬 + Ingr_镍
= 1000
碳最小占比: 0.008 Ingr_废料1 + 0.007 Ingr_废料2 + 0.0085 Ingr_废料3 + 0.004 Ingr_废料4
>= 6.5
碳最大占比: 0.008 Ingr_废料1 + 0.007 Ingr_废料2 + 0.0085 Ingr_废料3 + 0.004 Ingr_废料4
<= 7.5
镍最小占比: 0.18 Ingr_废料1 + 0.032 Ingr_废料2 + Ingr_镍 >= 30
镍最大占比: 0.18 Ingr_废料1 + 0.032 Ingr_废料2 + Ingr_镍 <= 35
铬最小占比: 0.12 Ingr_废料1 + 0.011 Ingr_废料2 + Ingr_铬 >= 10
铬最大占比: 0.12 Ingr_废料1 + 0.011 Ingr_废料2 + Ingr_铬 <= 12
钼最小占比: 0.001 Ingr_废料2 + Ingr_钼 >= 11
钼最大占比: 0.001 Ingr_废料2 + Ingr_钼 <= 13
废料1可用量: Ingr_废料1 <= 75
废料2可用量: Ingr_废料2 <= 250
VARIABLES
Ingr_废料1 Continuous
Ingr_废料2 Continuous
Ingr_废料3 Continuous
Ingr_废料4 Continuous
Ingr_钼 Continuous
Ingr_铬 Continuous
Ingr_镍 Continuous
求解状态: Optimal
Ingr_废料1 = 75.0
Ingr_废料2 = 90.909091
Ingr_废料3 = 672.28283
Ingr_废料4 = 137.30808
Ingr_钼 = 10.909091
Ingr_铬 = 0.0
Ingr_镍 = 13.590909
最优总成本 = 9953.671725000002