MindOpt在使用单纯形法求解线性规划问题这一功能上已经取得了不错的成绩,但在实际生活中,可能会遇到一些结构特殊的线性规划问题,这类问题可能存在比单纯形法更加简便的算法。本篇小编将介绍MindOpt如何求解这么一类特殊结构的线性规划问题——运输问题。
本篇是运输问题系列的第一篇,案例在云上建模求解平台可供复制学习。
问题描述
一般的运输问题是计算如何把某个商品从几个不同的产地,运输到几个不同的目的地,并知道运输量需求、不同运输路径的成本,或者不同目的地的需求等信息。问题如下:
某公司在如下3个地方生产商品,并运送到另外7个地点进行销售,请问该公司运输货物的最低成本是多少?
在这个问题里,3个产地的商品产量如下:
地点 | 产量 |
---|---|
GARY | 1400吨 |
CLEV | 2600吨 |
PITT | 2900吨 |
以上的6900吨商品将被运送到7个地点进行销售,每个地点的需求量各不相同,如图所示:
地点 | 需求 |
---|---|
FRA | 900吨 |
DET | 1200吨 |
LAN | 600吨 |
WIN | 400吨 |
STL | 1700吨 |
FRE | 1100吨 |
LAF | 1000吨 |
已知从产地运输货物到销售地的每吨所需成本如下:
GARY | CLEV | PITT | |
---|---|---|---|
FRA | 39元/吨 | 27元/吨 | 24元/吨 |
DET | 14元/吨 | 9元/吨 | 14元/吨 |
LAN | 11元/吨 | 12元/吨 | 17元/吨 |
WIN | 14元/吨 | 9元/吨 | 13元/吨 |
STL | 16元/吨 | 26元/吨 | 28元/吨 |
FRE | 82元/吨 | 95元/吨 | 99元/吨 |
LAF | 8元/吨 | 17元/吨 | 20元/吨 |
数学规划模型
以上问题,我们可以建立线性规划的数学模型如下。
集合
- 起点集合 O O O
- 目的地集合 D D D
参数
- 起点 o ∈ O o \in O o∈O的商品产量 s o s_o so
- 目的地 d ∈ D d \in D d∈D的商品需求 d d d_d dd
- 每吨商品从起点 o ∈ O o \in O o∈O运输至目的地 d ∈ D d \in D d∈D所需成本 c o d c_{od} cod
决策变量
起点 o ∈ O o \in O o∈O运送到目的地 d ∈ D d \in D d∈D的商品数量 t o d ≥ 0 t_{od}\geq0 tod≥0
目标函数
工厂要最小化运输成本 min \min min ∑ o ∈ O , d ∈ D c o d ⋅ t o d \sum_{o\in O, d \in D} c_{od} \cdot t_{od} ∑o∈O,d∈Dcod⋅tod
约束
- 起点 o ∈ O o \in O o∈O运送到各个目的地货物的总量等于其商品产量 ∑ d ∈ D t o d = s o \sum_{d \in D}t_{od} =s_o ∑d∈Dtod=so
- 不同起点运送到目的地 d ∈ D d \in D d∈D的商品总量等于目的地商品的需求 ∑ o ∈ O t o d = d d \sum_{o \in O}t_{od} =d_d ∑o∈Otod=dd
使用MindOpt APL进行建模和求解
MindOpt建模语言(MindOpt Algebra Programming Language, MindOpt APL,简称为MAPL)是一种高效且通用的代数建模语言。当前主要用于数学规划问题的建模,并支持调用多种求解器求解。下面将演示如何使用MAPL将上面的数学模型公式和数据输入,生成一个求解器可求解的问题,再调用求解器去进行求解。
方法1:cell中直接输入建模代码运行
在求解数独问题时,MindOpt APL可以将其建模代码直接输入如下cell里运行。请注意内核(kernel)需要选MAPL。
其中,有一行我们增加do check的语句来进行验证,确保总产量等于总需求。
clear model;#清除model,多次run的时候使用
option modelname model/transport_01;#方便与方法2的中间文件生成在同一个目录
#---------建模-----------------
# transport_01.mapl
set ORIG := {"GARY", "CLEV", "PITT"};
set DEST := {"FRA", "DET", "LAN", "WIN", "STL", "FRE", "LAF"};
param supply[ORIG] := <"GARY"> 1400, <"CLEV"> 2600, <"PITT"> 2900;
param demand[DEST] := <"FRA"> 900, <"DET"> 1200, <"LAN"> 600, <"WIN"> 400, <"STL"> 1700, <"FRE"> 1100, <"LAF"> 1000;
param cost[ORIG * DEST] :=
| "FRA", "DET", "LAN", "WIN", "STL", "FRE", "LAF"|
|"GARY"| 39, 14, 11, 14, 16, 82, 8 |
|"CLEV"| 27, 9, 12, 9, 26, 95, 17 |
|"PITT"| 24, 14, 17, 13, 28, 99, 20 |;
check sum {<o> in ORIG } supply[o] == sum {<d> in DEST } demand[d]; #增加do check的语句来进行验证,确保总产量等于总需求
var Trans[<o, d> in ORIG * DEST] >= 0;
minimize Total_Cost: sum {<o, d> in ORIG * DEST } cost[o, d] * Trans[o, d];
subto Supply:
forall { <o> in ORIG }
sum {<d> in DEST } Trans[o, d] == supply[o];
subto Demand:
forall { <d> in DEST }
sum { <o> in ORIG } Trans[o, d] == demand[d];
#------------------------------
print "-----------------用MindOpt求解---------------";
option solver mindopt; # 指定求解用MindOpt求解器
solve; # 求解
display;
print "-----------------结果---------------";
print "最小成本 = ";
print sum { <o, d> in ORIG * DEST } cost[o, d] * Trans[o, d];
运行结果如下:
-----------------用MindOpt求解---------------
Running mindoptampl
wantsol=1
mip_integer_tolerance=1e-9
MindOpt Version 0.23.0 (Build date: 20221129)
Copyright (c) 2020-2022 Alibaba Cloud.
Start license validation (current time : 01-MAR-2023 20:37:59).
License validation terminated. Time : 0.004s
Model summary.
- Num. variables : 21
- Num. constraints : 10
- Num. nonzeros : 42
- Bound range : [4.0e+02,1.0e+20]
- Objective range : [8.0e+00,9.9e+01]
- Matrix range : [1.0e+00,1.0e+00]
Presolver started.
Presolver terminated. Time : 0.001s
Simplex method started.
Model fingerprint: ==gZ3V2Y3ZWZ3dmZ
Iteration Objective Dual Inf. Primal Inf. Time
0 0.00000e+00 0.0000e+00 1.3800e+04 0.01s
9 1.96200e+05 0.0000e+00 0.0000e+00 0.01s
Postsolver started.
Simplex method terminated. Time : 0.007s
OPTIMAL; objective 196200.00
9 simplex iterations
Completed.
Primal Solution:
Trans@<GARY,FRA> = 0.000000000000000E+00
Trans@<GARY,DET> = 0.000000000000000E+00
Trans@<GARY,LAN> = 0.000000000000000E+00
Trans@<GARY,WIN> = 0.000000000000000E+00
Trans@<GARY,STL> = 3.000000000000000E+02
Trans@<GARY,FRE> = 1.100000000000000E+03
Trans@<GARY,LAF> = 0.000000000000000E+00
Trans@<CLEV,FRA> = 0.000000000000000E+00
Trans@<CLEV,DET> = 1.200000000000000E+03
Trans@<CLEV,LAN> = 6.000000000000000E+02
Trans@<CLEV,WIN> = 4.000000000000000E+02
Trans@<CLEV,STL> = 0.000000000000000E+00
Trans@<CLEV,FRE> = 0.000000000000000E+00
Trans@<CLEV,LAF> = 4.000000000000000E+02
Trans@<PITT,FRA> = 9.000000000000000E+02
Trans@<PITT,DET> = 0.000000000000000E+00
Trans@<PITT,LAN> = 0.000000000000000E+00
Trans@<PITT,WIN> = 0.000000000000000E+00
Trans@<PITT,STL> = 1.400000000000000E+03
Trans@<PITT,FRE> = 0.000000000000000E+00
Trans@<PITT,LAF> = 6.000000000000000E+02
Dual Solution:
Supply_1 = 8.000000000000004E+00
Supply_2 = 1.700000000000000E+01
Supply_3 = 2.000000000000000E+01
Demand_1 = 3.999999999999999E+00
Demand_2 = -7.999999999999999E+00
Demand_3 = -5.000000000000000E+00
Demand_4 = -7.999999999999999E+00
Demand_5 = 7.999999999999998E+00
Demand_6 = 7.399999999999999E+01
Demand_7 = 0.000000000000000E+00
-----------------结果---------------
最小成本 =
196200
方法2:导入.mapl文件运行建模然后求解
上面时直接在cell中运行所有的脚本,我们也可以建立个新文档,将中间建模的代码保存为 transport_01.mapl
文件。然后运行如下code,结果同方法1。
clear model;#清除model,多次run的时候使用
#------------------------------
model model/transport_01.mapl; #通过导入建模脚本.mapl文件进行建模
#------------------------------
print "-----------------用MindOpt求解---------------";
option solver mindopt; # 指定求解用MindOpt求解器
solve; # 求解
display;
print "-----------------结果---------------";
print "最小成本 = ";
print sum { <o, d> in ORIG * DEST } cost[o, d] * Trans[o, d];
结果解析
display指令运行时,会打印出很多求解的结果,Trans@name 是决策变量的取值,后面的dual solution是对偶解的值。示意如下:
Primal Solution:
Trans@<GARY,FRA> = 0.000000000000000E+00
Trans@<GARY,DET> = 0.000000000000000E+00
Trans@<GARY,LAN> = 0.000000000000000E+00
Trans@<GARY,WIN> = 0.000000000000000E+00
Trans@<GARY,STL> = 3.000000000000000E+02
Trans@<GARY,FRE> = 1.100000000000000E+03
Trans@<GARY,LAF> = 0.000000000000000E+00
Trans@<CLEV,FRA> = 0.000000000000000E+00
Trans@<CLEV,DET> = 1.200000000000000E+03
Trans@<CLEV,LAN> = 6.000000000000000E+02
Trans@<CLEV,WIN> = 4.000000000000000E+02
Trans@<CLEV,STL> = 0.000000000000000E+00
Trans@<CLEV,FRE> = 0.000000000000000E+00
Trans@<CLEV,LAF> = 4.000000000000000E+02
Trans@<PITT,FRA> = 9.000000000000000E+02
Trans@<PITT,DET> = 0.000000000000000E+00
Trans@<PITT,LAN> = 0.000000000000000E+00
Trans@<PITT,WIN> = 0.000000000000000E+00
Trans@<PITT,STL> = 1.400000000000000E+03
Trans@<PITT,FRE> = 0.000000000000000E+00
Trans@<PITT,LAF> = 6.000000000000000E+02
Dual Solution:
Supply_1 = 8.000000000000004E+00
Supply_2 = 1.700000000000000E+01
Supply_3 = 2.000000000000000E+01
Demand_1 = 3.999999999999999E+00
Demand_2 = -7.999999999999999E+00
Demand_3 = -5.000000000000000E+00
Demand_4 = -7.999999999999999E+00
Demand_5 = 7.999999999999998E+00
Demand_6 = 7.399999999999999E+01
Demand_7 = 0.000000000000000E+00
-----------------结果---------------
最小成本 =
196200
同时,在最近建模的文件所在目录或option modelname指定的位置,会生成对应的文件 .nl
和 .sol
。其中 .nl
文件是建模的问题模型文件,可被多数求解器识别,.sol
文件中存储了求解结果solution。
从打印的结果,我们可以得到该公司的最低运输成本为196200元。并且此成本时,运输方案是:
Trans@<CLEV,DET> = 1.200000000000000E+03
Trans@<CLEV,LAF> = 4.000000000000000E+02
Trans@<CLEV,LAN> = 6.000000000000000E+02
Trans@<CLEV,WIN> = 4.000000000000000E+02
Trans@<GARY,FRE> = 1.100000000000000E+03
Trans@<GARY,STL> = 3.000000000000000E+02
Trans@<PITT,FRA> = 9.000000000000000E+02
Trans@<PITT,LAF> = 6.000000000000000E+02
Trans@<PITT,STL> = 1.400000000000000E+03