Python Pulp库求解线性规划问题(三) 离散优化模型的求解

问题描述

之前面对的决策变量都是连续的,但是在实际应用中,很多决策变量都是完全离散的。譬如背包问题(Knapsack problem)中,我们需要在背包大小限制了我们所能携带的物品重量或者体积的情况下,选择出要携带的最有价值的一组物品。

这里的案例问题来自《运筹学》第二版中的NASA资产预算问题。其描述如下:

假设NASA需要在许多互相竞争的航天任务中分配预算,下表显示了这些航天任务的价值评估以及相互关系,预算的单位为十万美元:

j 任务 阶段1预算 阶段2预算 阶段3预算 阶段4预算 阶段5预算 价值 互斥任务 依赖任务
1 通信卫星 6 - - - - 200 - -
2 轨道微波 2 3 - - - 3 - -
3 木卫一登录器 3 5 - - - 20 - -
4 2020天王星轨道卫星 - - - - 10 50 5 3
5 2010天王星轨道卫星 - 5 8 - - 70 4 3
6 水星探测器 - - 1 8 4 20 - 3
7 土星探测器 1 8 - - - 5 - 3
8 红外成像 - - - 5 - 10 11 -
9 陆基外星智能探测计划 4 5 - - - 200 14 -
10 大型轨道结构 - 8 4 - - 150 - -
11 彩色成像 - - 2 7 - 18 8 2
12 医疗技术 5 7 - - - 8 - -
13 极轨道平台 - 1 4 1 1 300 - -
14 对地同步的外星智能探测计划 - 4 5 3 3 185 9 -
预算约束 10 12 14 14 14

需要在给定的预算约束下,选择价值最大的一组项目。

决策变量

问题的决策变量比较简单,我们需要挑选的项目,可以表达为:
x_j=\begin{cases} 0& 任务j被选中\\ 1& 任务j未被选中 \end{cases}
其中j=1,2,3...,14

目标函数

这里是一个最大问题,可以表达为:
max\ 200x_1+3x_2+20x_3+50x_4+70x_5+20x_6+5x_7+10x_8 \\+200x_9+150x_{10}+18x_{11}+8x_{12}+300x_{13}+185x_{14}

约束条件

此问题中约束条件分为三组:预算约束、互斥约束和依赖约束。

预算约束即每个阶段中选中的任务预算总和不能超过约束:
6x_1+2x_2+3x_3+x_7+4x_9+5x_{12}\le 10 \\3x_2+5x_3+5x_5+8x_7+5x_9+8x_{10}+7x_{12}+x_{13}+4x_{14}\le 12 \\8x_5+x_6+4x_{10}+2x_{11}+4x_{13}+5x_{14}\le 14 \\8x_6+5x_8+7x_{11}+x_{13}+3x_{14}\le 14 \\10x_4+4x_6+x_{13}+3x_{14}\le 14
互斥约束即两个变量至多选择一个,可以用\sum x_j\le 1的形式来表达:
x_4+x_5\le 1 \\x_8+x_{11}\le 1 \\x_9+x_{14}\le 1
依赖约束即选择x_i是选择x_j的前提,可以用x_i\le x_j的形式来表达:
x_4\le x_3 \\x_5\le x_3 \\x_6\le x_3 \\x_7\le x_3 \\x_{11}\le x_2

编程求解

在明确了问题之后,编程求解是相对容易的。这里需要注意的是我们的变量因为是0-1变量,因此在设置变量类型时,需要使用LpBinary而非之前使用的LpContinuous

from pulp import *

# 1. 建立问题
model = LpProblem("NASA资产预算问题", LpMaximize)

# 2. 建立变量
tasks = ["通信卫星", "轨道微波", "木卫一登陆器", "2020天王星轨道卫星", "2010天王星轨道卫星", "水星探测器", "土星探测器", 
                "红外成像", "陆基外星智能探测计划", "大型轨道结构", "彩色成像", "医疗技术", "极轨道平台", "对地同步的外星智能探测计划" ]
taskVar = LpVariable.dicts("Tasks", tasks, lowBound=0, cat=LpBinary)

# 3. 设置目标函数
values = {"通信卫星": 200, 
                   "轨道微波": 3, 
                   "木卫一登陆器": 20, 
                   "2020天王星轨道卫星": 50, 
                   "2010天王星轨道卫星": 70, 
                   "水星探测器": 20, 
                   "土星探测器": 5, 
                   "红外成像": 10, 
                   "陆基外星智能探测计划": 200, 
                   "大型轨道结构": 150, 
                   "彩色成像": 18, 
                   "医疗技术": 8, 
                   "极轨道平台": 300, 
                   "对地同步的外星智能探测计划": 185}
model +=  lpSum([values[item] * taskVar[item] for item in tasks]), "项目总价值"

# 4. 施加约束
stage1 = {"通信卫星": 6, 
                   "轨道微波": 2, 
                   "木卫一登陆器": 3, 
                   "2020天王星轨道卫星": 0, 
                   "2010天王星轨道卫星": 0, 
                   "水星探测器": 0, 
                   "土星探测器": 1, 
                   "红外成像": 0, 
                   "陆基外星智能探测计划": 4, 
                   "大型轨道结构": 0, 
                   "彩色成像": 0, 
                   "医疗技术": 5, 
                   "极轨道平台": 0, 
                   "对地同步的外星智能探测计划": 0}

stage2 = {"通信卫星": 0, 
                   "轨道微波": 3, 
                   "木卫一登陆器": 5, 
                   "2020天王星轨道卫星": 0, 
                   "2010天王星轨道卫星": 5, 
                   "水星探测器": 0, 
                   "土星探测器": 8, 
                   "红外成像": 0, 
                   "陆基外星智能探测计划": 5, 
                   "大型轨道结构": 8, 
                   "彩色成像": 0, 
                   "医疗技术": 7, 
                   "极轨道平台": 1, 
                   "对地同步的外星智能探测计划": 4}

stage3 = {"通信卫星": 0, 
                   "轨道微波": 0, 
                   "木卫一登陆器": 0, 
                   "2020天王星轨道卫星": 0, 
                   "2010天王星轨道卫星": 8, 
                   "水星探测器": 1, 
                   "土星探测器": 0, 
                   "红外成像": 0, 
                   "陆基外星智能探测计划": 0, 
                   "大型轨道结构": 4, 
                   "彩色成像": 2, 
                   "医疗技术": 0, 
                   "极轨道平台": 4, 
                   "对地同步的外星智能探测计划": 5}

stage4 = {"通信卫星": 0, 
                   "轨道微波": 0, 
                   "木卫一登陆器": 0, 
                   "2020天王星轨道卫星": 0, 
                   "2010天王星轨道卫星": 0, 
                   "水星探测器": 8, 
                   "土星探测器": 0, 
                   "红外成像": 5, 
                   "陆基外星智能探测计划": 0, 
                   "大型轨道结构": 0, 
                   "彩色成像": 7, 
                   "医疗技术": 0, 
                   "极轨道平台": 1, 
                   "对地同步的外星智能探测计划": 3}

stage5 = {"通信卫星": 0, 
                   "轨道微波": 0, 
                   "木卫一登陆器": 0, 
                   "2020天王星轨道卫星": 10, 
                   "2010天王星轨道卫星": 0, 
                   "水星探测器": 4, 
                   "土星探测器": 0, 
                   "红外成像": 0, 
                   "陆基外星智能探测计划": 0, 
                   "大型轨道结构": 0, 
                   "彩色成像": 0, 
                   "医疗技术": 0, 
                   "极轨道平台": 1, 
                   "对地同步的外星智能探测计划": 3}

model += lpSum([stage1[item]*taskVar[item] for item in tasks]) <= 10, "阶段1"
model += lpSum([stage2[item]*taskVar[item] for item in tasks]) <= 12, "阶段2"
model += lpSum([stage3[item]*taskVar[item] for item in tasks]) <= 14, "阶段3"
model += lpSum([stage4[item]*taskVar[item] for item in tasks]) <= 14, "阶段4"
model += lpSum([stage5[item]*taskVar[item] for item in tasks]) <= 14, "阶段5"
model += taskVar['2020天王星轨道卫星'] + taskVar['2010天王星轨道卫星'] <= 1, "4与5互斥"
model += taskVar['红外成像'] + taskVar['彩色成像'] <= 1, "8与11互斥"
model += taskVar['2020天王星轨道卫星'] <= taskVar['木卫一登陆器'] , "4依赖3"
model += taskVar['2010天王星轨道卫星'] <= taskVar['木卫一登陆器'] , "5依赖3"
model += taskVar['水星探测器'] <= taskVar['木卫一登陆器'] , "6依赖3"
model += taskVar['土星探测器'] <= taskVar['木卫一登陆器'] , "7依赖3"
model += taskVar['彩色成像'] <= taskVar['轨道微波'] , "11依赖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))

结果如下:

求解状态: Optimal
Tasks_2010天王星轨道卫星 = 0.0
Tasks_2020天王星轨道卫星 = 0.0
Tasks_医疗技术 = 0.0
Tasks_土星探测器 = 0.0
Tasks_大型轨道结构 = 0.0
Tasks_对地同步的外星智能探测计划 = 1.0
Tasks_彩色成像 = 0.0
Tasks_木卫一登陆器 = 0.0
Tasks_极轨道平台 = 1.0
Tasks_水星探测器 = 0.0
Tasks_红外成像 = 1.0
Tasks_轨道微波 = 0.0
Tasks_通信卫星 = 1.0
Tasks_陆基外星智能探测计划 = 1.0
最优总价值 =  895.0

因此最后的方案是选择14 - 对地同步的外星智能探测计划、13 - 极轨道平台、8 - 红外成像、1 - 通信卫星和9 - 陆基外星智能探测计划。在5个阶段的预算分别为10、10、9、9、4。

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

推荐阅读更多精彩内容