仓库选址规划
仓库选址问题是一个重要的运筹学问题,它涉及到在一个给定的地理区域中选择最佳的仓库位置以最小化总成本或者提高效率。仓库选址问题在现代物流和供应链管理中具有重要的应用,因为仓库的位置直接影响到货物的运输成本、交货时间和库存量等因素。
为了解决这个问题,我们可以使用数学规划的方法,通过建立一个数学模型来优化仓库位置和相应的服务水平。这个仓库选址模型可以考虑多种因素,如仓库建设、运输成本、库存成本、交货需求等。这些因素可以通过如下方式进行量化和搜集数据。
- 仓库建设成本:建设仓库的直接成本,包括土地成本、建筑成本、设备成本等。在实际应用中,这个成本因素通常是已知的,因为仓库建设成本是比较稳定的,可以通过市场价格或者预算来确定。
- 运输成本:将货物从仓库运输到客户所需的成本。这个成本因素通常包括运输距离、运输方式、运输时间、运输成本等因素。在实际应用中,这个成本因素通常是在经过一定的调查和分析之后得出的,可以通过运输网络模型、运输成本数据库或者实地考察等方式来确定。
- 库存成本:库存成本是在仓库中维护和管理存货所需的成本。这个成本因素包括存储成本、订单处理成本、库存租金、保险费用等。在实际应用中,我们需要通过调查和分析得出这些成本因素,以便在模型中考虑到它们对总成本的影响。
- 交货需求:交货需求是指客户对货物的需求量。这个数据可以通过客户的订单量、历史数据等方式来获取。在实际应用中,我们需要考虑到客户对不同仓库的需求量,以便在模型中考虑到它对总成本的影响。
下面我们将通过一个简单的例子,讲解如何使用数学规划的方法来建模仓库选址的问题,并调用MindOpt APL来编程和求解问题。
1. 问题描述
考虑如下决策问题,某公司拥有9家商店,现需要建造工厂为商店提供商品。已知有ABCD四种工厂类型,每种工厂最多建造一个,建造不同的工厂的成本各不相同,A需要500,B需要600,C需要700,D需要800.不同的工厂拥有不同的库存容量,A为40,B为55,C为73,D为90。对于9家商店而言,其对商品的需求量各不相同,具体如下表所示:
商店 | 商品需求 |
---|---|
1 | 10 |
2 | 14 |
3 | 17 |
4 | 8 |
5 | 9 |
6 | 12 |
7 | 11 |
8 | 15 |
9 | 16 |
并且,不同的工厂将货物运送到不同商店的代价也不相同,如下表所示:
商店1 | 商店2 | 商店3 | 商店4 | 商店5 | 商店6 | 商店7 | 商店8 | 商店9 | |
---|---|---|---|---|---|---|---|---|---|
A | 55 | 4 | 17 | 33 | 47 | 98 | 19 | 10 | 6 |
B | 42 | 12 | 4 | 23 | 16 | 78 | 47 | 9 | 82 |
C | 17 | 34 | 65 | 25 | 7 | 67 | 45 | 13 | 54 |
D | 60 | 8 | 79 | 24 | 28 | 19 | 62 | 18 | 45 |
本篇案例为了简化问题,我们假设每家商店仅能由一家工厂供货。
请问该建造哪些工厂,以及决定这些工厂向哪些商店供货,才能使得总成本最低?
2. 数学规划模型
以上问题的数学模型如下。
集合
- 工厂集合 P P P
- 商店集合 S S S
参数
- 每种工厂 p ∈ P p \in P p∈P的建造成本为 b p b_p bp
- 每种工厂 p ∈ P p\in P p∈P的库存容量为 c P c_P cP
- 每家商店 s ∈ S s \in S s∈S的商品需求为 d s d_s ds
- 从工厂 p ∈ P p \in P p∈P将商品运送到商店 s ∈ S s \in S s∈S所花费的代价为 t p s t_{ps} tps
决策变量
- 二进制决策变量 z p ∈ { 0 , 1 } , ∀ { p ∈ P } z_p\in \{0,1\},\forall\{p \in P\} zp∈{0,1},∀{p∈P}。如果 z p = 1 z_p=1 zp=1,则表示建造了 p p p工厂;如果 z p = 0 z_p=0 zp=0,则表示没有建造 p p p工厂。
- 二进制决策变量 x p s ∈ { 0 , 1 } , ∀ { p ∈ P } , ∀ { s ∈ S } x_{ps} \in \{0,1\},\forall\{p \in P\},\forall\{s \in S\} xps∈{0,1},∀{p∈P},∀{s∈S}。如果 x p s = 1 x_{ps}=1 xps=1,则表示 p p p工厂将运送商品给 s s s商店;如果 x p s = 0 x_{ps}=0 xps=0,则表示 p p p工厂没有把商品运送给 s s s商店。
目标函数
最小化建造及运输代价: min ( ∑ p ∈ P b p z p + ∑ p ∈ P , s ∈ S t p s x p s ) \min(\, \sum_{p \in P}b_p z_p+\sum_{p \in P,s \in S}t_{ps}x_{ps}) min(∑p∈Pbpzp+∑p∈P,s∈Stpsxps)
约束
- 每个商场仅有一个供货工厂: ∑ p ∈ P x p s = 1 , ∀ s ∈ S \sum_{p \in P}x_{ps} = 1, \forall {s \in S} ∑p∈Pxps=1,∀s∈S
- 只有建设了工厂才能提供货物给商店: x p s ≤ z p , ∀ { p ∈ P } , ∀ { s ∈ S } x_{ps} \leq z_p,\forall\{p \in P\},\forall\{s \in S\} xps≤zp,∀{p∈P},∀{s∈S}
- 每间工厂的库存不能小于其所提供的商店的需求之和: ∑ s ∈ S d s x p s ≤ c p , ∀ { p ∈ P } \sum_{ s \in S} d_s x_{ps}\leq c_p,\forall\{p \in P\} ∑s∈Sdsxps≤cp,∀{p∈P}
这里我们汇总的等价数学建模公式是
3. MindOpt APL 建模和求解
MindOpt APL是一款代数建模语言,它可以方便地将数学语言描述成程序,然后调用多种求解器求解。MindOpt Solver对网络流的线性规划求解效率很不错,且能支持大规模的问题。
改写上面的数据图和数学模型,如下代码,在Notebook的cell中运行它:
clear model;
# 建模
# factory.mapl
# 声明集合
set PLANTS := { "A" , "B" , "C" , "D" };
set STORES := { 1 .. 9 };
# 声明参数
param building[PLANTS]:= <"A"> 500 , <"B"> 600 , <"C"> 700 , <"D"> 800;
param capacity[PLANTS]:= <"A"> 40 , <"B"> 55 , <"C"> 73 , <"D"> 90;
param demand [STORES] := <1> 10 , <2> 14 ,
<3> 17 , <4> 8 ,
<5> 9 , <6> 12 ,
<7> 11 , <8> 15 ,
<9> 16;
param transport[PLANTS * STORES] :=
|1, 2, 3, 4, 5, 6, 7, 8, 9|
|"A" |55, 4, 17, 33, 47, 98, 19, 10, 6|
|"B" |42, 12, 4, 23, 16, 78, 47, 9, 82|
|"C" |17, 34, 65, 25, 7, 67, 45, 13, 54|
|"D" |60, 8, 79, 24, 28, 19, 62, 18, 45|;
# 声明变量
var z[PLANTS] binary;
var x[PLANTS * STORES] binary;
# 声明目标
minimize cost: sum {<p> in PLANTS } building[p] * z[p]
+ sum {<p,s> in PLANTS * STORES } transport[p,s] * x[p,s];
# 声明约束
subto assign:
forall {<s> in STORES }
sum {<p> in PLANTS } x[p,s] == 1;
subto build:
forall {<p,s> in PLANTS * STORES }
z[p] >= x[p,s] ;
subto limit:
forall {<p> in PLANTS }
capacity[p] >= sum {<s> in STORES } demand[s] * x[p,s];
print "-----------------用MindOpt求解---------------";
option solver mindopt; # (可选)指定求解用的求解器,默认是MindOpt
#option mindopt_options 'print=0'; #设置求解器输出级别,减少过程打印
solve; # 求解
# 可以切换其他的求解器
#option solver cbc;
#option cbc_options 'log=0'; #设置求解器输出级别,减少过程打印
#solve; # 求解
print "-----------------Display---------------";
display; # 展示结果
print "经过优化后,最小成本=" ,sum {<p> in PLANTS } building[p] * z[p]
+ sum {<p,s> in PLANTS * STORES } transport[p,s] * x[p,s];
print "其中,建立的工厂和对应的运输路线是:";
forall {<p,s>in PLANTS*STORES with z[p]>=1 and x[p,s] >= 1}
print p,",供货给商店:",s;
运行代码结果为:
-----------------用MindOpt求解---------------
Running mindoptampl
wantsol=1
MindOpt Version 0.24.1 (Build date: 20230423)
Copyright (c) 2020-2023 Alibaba Cloud.
Start license validation (current time : 05-JUL-2023 20:38:44).
License validation terminated. Time : 0.006s
Model summary.
- Num. variables : 40
- Num. constraints : 49
- Num. nonzeros : 144
- Num. integer vars. : 40
- Bound range : [1.0e+00,9.0e+01]
- Objective range : [4.0e+00,8.0e+02]
Branch-and-cut method started.
Original model: nrow = 49 ncol = 40 nnz = 144
Tolerance: primal = 1e-06 int = 1e-06 mipgap = 0 mipgapAbs = 0.0001
Limit: time = 1.79769313486232e+308 node = -1 stalling = -1 solution = -1
presolver terminated; took 0 ms
presolver terminated; took 1 ms
Parallelism: root=1, tree=1
tree 0 node 0 accept new sol: obj 2056 (heur) bnd vio 0 int vio 0 mipgap 4.86381322957198e+96 time 0
Model summary.
- Num. variables : 40
- Num. constraints : 49
- Num. nonzeros : 144
- Bound range : [1.0e+00,9.0e+01]
- Objective range : [4.0e+00,8.0e+02]
- Matrix range : [1.0e+00,1.7e+01]
Presolver started.
Presolver terminated. Time : 0.000s
Simplex method started.
Model fingerprint: =Y2djNmZ3d2Y352Y
Iteration Objective Dual Inf. Primal Inf. Time
0 0.00000e+00 0.0000e+00 9.0000e+00 0.00s
41 8.86018e+02 0.0000e+00 0.0000e+00 0.00s
Postsolver started.
Simplex method terminated. Time : 0.002s
tree 0 #node(P:0 Q:0) #(dep:0 max:0) #(dual:886.017857142857 best:2056 gap:56%) #(conf:0 cut:0) #time = 0
tree 0 node 0 accept new sol: obj 1854 (heur) bnd vio 0 int vio 0 mipgap 0.522104715672677 time 0
tree 0 node 0 accept new sol: obj 1743 (node) bnd vio 0 int vio 0 mipgap 0.491670764691419 time 0
tree 0 node 0 accept new sol: obj 1567 (heur) bnd vio 0 int vio 0 mipgap 0.43457698969824 time 0
tree 0 node 0 accept new sol: obj 1538 (node) bnd vio 0 int vio 0 mipgap 0.423915567527401 time 0
tree 0 node 0 accept new sol: obj 1457 (heur) bnd vio 2.22044604925031e-16 int vio 8.88178419700125e-16 mipgap 0.391888910677517 time 0
Restart
presolver terminated; took 1 ms
tree 1 node -1 accept new sol: obj 1457 (node) bnd vio 8.88178419700125e-16 int vio 8.88178419700125e-16 mipgap 6.86341798215511e+96 time 0
Parallelism: root=1, tree=1
tree 1 #node(P:0 Q:0) #(dep:0 max:0) #(dual:1067.39285714286 best:1457 gap:26%) #(conf:0 cut:65) #time = 0
Branch-and-cut method terminated. Time : 1.793s
OPTIMAL; objective 1457.00
Completed.
-----------------Display---------------
Primal Solution:
z@A = 1
z@B = 0
z@C = 1
z@D = 0
x@<A,1> = 0
x@<A,2> = 1
x@<A,3> = 1
x@<A,4> = 1
x@<A,5> = 0
x@<A,6> = 0
x@<A,7> = 0
x@<A,8> = 0
x@<A,9> = 0
x@<B,1> = 0
x@<B,2> = 0
x@<B,3> = 0
x@<B,4> = 0
x@<B,5> = 0
x@<B,6> = 0
x@<B,7> = 0
x@<B,8> = 0
x@<B,9> = 0
x@<C,1> = 1
x@<C,2> = 5.07530526e-16
x@<C,3> = 0
x@<C,4> = 2.22044605e-16
x@<C,5> = 1
x@<C,6> = 1
x@<C,7> = 1
x@<C,8> = 1
x@<C,9> = 1
x@<D,1> = 8.8817842e-16
x@<D,2> = 0
x@<D,3> = 0
x@<D,4> = 0
x@<D,5> = 0
x@<D,6> = 0
x@<D,7> = 0
x@<D,8> = 0
x@<D,9> = 0
经过优化后,最小成本=1457
其中,建立的工厂和对应的运输路线是:
A,供货给商店:2
A,供货给商店:3
A,供货给商店:4
C,供货给商店:1
C,供货给商店:5
C,供货给商店:6
C,供货给商店:7
C,供货给商店:8
C,供货给商店:9
4. 结果
运行上述代码后,得到结果为:
经过优化后,最小成本=1457
其中,建立的工厂和对应的运输路线是:
A,供货给商店:2
A,供货给商店:3
A,供货给商店:4
C,供货给商店:1
C,供货给商店:5
C,供货给商店:6
C,供货给商店:7
C,供货给商店:8
C,供货给商店:9
即,最优的方案是选择A、C两个地方建厂,然后2、3、4商店的货由A厂提供,其他的厂的货由C厂提供。