Python Pulp库求解线性规划问题(二) 从字典创建模型

在之前的例子中,我们是用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

决策变量

在这个问题中,我们可以将各材料的质量作为决策变量:
x_j\triangleq 生产中使用原料j的质量(千克)
其中j=1,2,3,4代表4种废料,j=5,6,7代表3种添加剂。

目标函数

这个问题中目标函数很明显时使成本最小化,可以用决策变量表达为:
min\ 16x_1+10x_2+8x_3+9x_4+48x_5+60x_6+53x_7

约束条件

质量约束:
\sum_jx_j=1000,j=1,2,3,...,7
成分约束:
0.008x_1+0.007x_2+0.0085x_3+0.004x_4\ge 0.0065*1000 \\0.008x_1+0.007x_2+0.0085x_3+0.004x_4\le 0.0075*1000 \\0.18x_1+0.032x_2+x5\ge 0.03*1000 \\0.18x_1+0.032x_2+x5\le 0.035*1000 \\0.12x_1+0.011x_2+x6\ge 0.01*1000 \\0.12x_1+0.011x_2+x6\le 0.012*1000 \\0.001x_2+x_7\ge 0.011*1000 \\0.001x_2+x_7\le 0.013*1000
变量上下界:
0\le x_j, j=1,2,3,...,7 \\x_1\le 75 \\ x_2\le 250

编程求解

对于以上的过程,我们利用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
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,451评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,172评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,782评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,709评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,733评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,578评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,320评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,241评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,686评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,878评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,992评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,715评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,336评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,912评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,040评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,173评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,947评论 2 355

推荐阅读更多精彩内容