初识随机规划 (2):两阶段随机规划(一个详细的例子)
本文中的确定性问题的例子来源于《运筹学》第四版,运筹学编写组编著。
两阶段随机规划模型
上篇文章介绍了随机规划
的一个小例子。
文章链接: https://mp.weixin.qq.com/s/9RsWE6E00W_dz7bpThHInA 或者 https://blog.csdn.net/HsinglukLiu/article/details/112150067
为了方便阅读,我们把那个例子拿过来,再复习一遍。
一个工厂生产2种产品 A A A和 B B B, A A A产品需要1个单位人工工时和4个单位的设备1的工时, B B B产品需要2个单位的人工工时和4个单位的设备2的工时。且该厂一共可提供人工工时、设备1工时和设备2的工时分别为8,16和12。且生产单位产品 A A A、 B B B的净利润分别为2和3。假设该工厂生产的产品是供不应求的 ,问,该工厂应该如何生产(可以是连续的量),使得净利润最大化。
根据上述描述,我们引入下面的决策变量:
x 1 , x 2 x_1, x_2 x1,x2分别代表生产产品 A 、 B A、B A、B的量,则该问题的数学模型为
max 2 x 1 + 3 x 2 s . t . x 1 + 2 x 2 ⩽ 8 4 x 1 ⩽ 16 4 x 2 ⩽ 12 x 1 , x 2 ⩾ 0 \begin{aligned} \max \,\,&2x_1 + 3x_2 \\ s.t. \quad &x_1 + 2x_2 \leqslant 8 \\ &4x_1 \qquad \leqslant 16 \\ &\qquad 4x_2 \leqslant 12 \\ &x_1, x_2 \geqslant 0 \end{aligned} maxs.t.2x1+3x2x1+2x2⩽84x1⩽164x2⩽12x1,x2⩾0
但是上面的文章只是让读者直观的感受一下随机规划究竟是什么,文中的随机规划模型如下:
max ∑ k ∈ K p k ( c 1 x 1 k + c 2 x 2 k ) s . t . a 11 k x 1 k + a 12 k x 2 k ⩽ 8 , ∀ k ∈ K a 21 k x 1 k ⩽ 16 , ∀ k ∈ K a 32 k x 2 ⩽ 12 , ∀ k ∈ K x 1 k , x 2 k ⩾ 0 , ∀ k ∈ K \begin{aligned} \max \,\,& \sum_{k \in K}p_k (c_1 x_1^k + c_2 x_2^k ) \\ s.t. \quad & a_{11}^k x_1^k + a_{12}^kx_2^k \leqslant 8, \quad \forall k \in K \\ &a_{21}^kx_1^k \qquad\,\,\,\,\,\,\, \leqslant 16, \quad \forall k \in K \\ &\,\,\,\,\,\,\,\,\,\,\qquad a_{32}^kx_2 \leqslant 12, \quad \forall k \in K \\ &x_1^k, x_2^k \geqslant 0, \quad \forall k \in K \end{aligned} maxs.t.k∈K∑pk(c1x1k+c2x2k)a11kx1k+a12kx2k⩽8,∀k∈Ka21kx1k⩽16,∀k∈Ka32kx2⩽12,∀k∈Kx1k,x2k⩾0,∀k∈K
其紧凑形式为
max ∑ k ∈ K p k ∑ i ∈ I c i x i k s . t . ∑ i ∈ I a j i k x i k ⩽ b j , ∀ j ∈ J , ∀ k ∈ K x i k ⩾ 0 , ∀ i ∈ I , ∀ k ∈ K \begin{aligned} \max \,\,& \sum_{k \in K}p_k \sum_{i \in I}c_i x_i^k \\ s.t. &\quad \sum_{i \in I}{a_{ji}^k x_i^k} \leqslant b_j, \qquad \forall j \in J, \forall k \in K \\ &x_i^k \geqslant 0, \qquad \qquad \forall i \in I, \forall k \in K \end{aligned} maxs.t.k∈K∑pki∈I∑cixiki∈I∑ajikxik⩽bj,∀j∈J,∀k∈Kxik⩾0,∀i∈I,∀k∈K
其中 I = { 1 , 2 } I = \{1, 2\} I={ 1,2}表示产品的集合, J = { 1 , 2 , 3 } J = \{1, 2, 3\} J={ 1,2,3}表示资源的集合。 a j i k a_{ji}^k ajik表示第 j ∈ J j \in J j∈J种资源被第 i ∈ I i \in I i∈I种商品在第 k ∈ K k \in K k∈K个场景下需要的量。
细心的读者会发现,上面的模型,其实可以等价为:分别求解 K K K个线性规划,然后将 K K K个单独的线性规划的目标值加权求和一下。原因是这 K K K个场景下的模型没有任何联系,是相互独立的。
难道说随机规划就是很多个确定性线性规划简单的分别求解一下,然后把目标函数加和一下这么简单吗?当然不是。
3层决策:战略层(strategic)
,战术层(tactical)
和作业层(operational)
一般情况下,随机规划都是有多个阶段的决策的。一般来讲,决策有三个层次:
战略层(strategic)
,战术层(tactical)
和作业层(operational)
。从strategic
到tactical
再到operational
,决策的周期越来越短,决策的事情越来越具体化。随机规划中,比较常见的是两个阶段的决策two-stage decision
,因此,读论文的时候,经常看到关于随机规划的文章是把问题建模为一个two-stage mixed integer programming
的模型求解的。
所谓的two-stage
,一般可以认为包含两种决策,一种是比较长期的 ,上层的决策,比如服务网络怎么搭建,物流网络怎么搭建,基础设施怎么搭建等,这种决策会影响企业较长时间的发展,我们称其为战略层决策(strategic level)
,有的决策也许没有到战略层的级别,可以称为战术层决策(tactical level)
。第二种决策就是非常具体化的,比如生产计划,采购计划,VRP的路径规划等,都是非常具体、细节的决策,这一类决策对企业的发展影响周期较短,可以称为作业层决策(operational level)
。为了方便统一,本文中我们统一使用tactical level
和operational level
来表示上层决策和下层决策。
Two-stage Stochastic Programming
Two-stage Stochastic Programming
的直观写法
还是回到随机规划上来。我们回去看之前的例子,例子中说,我们有生产产品 A A A和产品 B B B的设备1和设备2,有了设备,我们可以决定生产 A A A和 B B B。但是在实际中,也许我们可以再往前座一层决策,那就是,我们决策一下,要不要接产品 A A A和产品 B B B的订单,也就是要不要生产产品 A A A和产品 B B B。例如,如果要生产 A A A,我们就需要准备生产 A A A所需的设备等,这些活动是需要花费一些成本的,我们可以认为这些成本是固定的,即固定成本
或者启动成本
,固定成本可以认为就是需要去购置机器,雇佣人员等。这个决策,可以看做是上层决策(tactical level)
,很明显,这个决策并没有涉及具体生产多少产品的决策。
在决定了要生产 A A A以后,也准备好了所有的所需材料和人工,我们基于这些材料,再去制定具体要生产多少商品的生产计划。这个生产计划,就是非常具体的决策,可以看做是作业层决策(operational level)
, 即下层决策。
假设决定生产产品 A A A和产品 B B B的固定成本分别为 f A f_A fA和 f B f_B fB。我们引入上层决策(tactical level)
的决策变量:
z i ∈ { 0 , 1 } , i = A or B \begin{aligned} z_i \in \{ 0, 1\}, i = A \text{ or } B \end{aligned} zi∈{
0,1},i=A or B
表示我们决定是否生产产品 A A A或者 B B B。
之后,具体操作层的决策,就是每种产品生产多少,即为 x i k x_i^k xik,在第 k k k个场景下,生产产品 i i i的数量。
因此,Two-stage Stochastic Programming
的模型可以写成
max ∑ k ∈ K p k ( ∑ i ∈ I c i x i k ) − ∑ i ∈ I f i z i s . t . ∑ i ∈ I a j i k x i k ⩽ b j , ∀ j ∈ J , ∀ k ∈ K ∑ k ∈ K x i k − M z i ⩽ 0 , ∀ i ∈ I z i ∈ { 0 , 1 } , x i k ⩾ 0 , ∀ i ∈ I , ∀ k ∈ K \begin{aligned} \max \,\,&\sum_{k\in K}{p_k}\left( \sum_{i\in I}{c_i}x_{i}^{k} \right) -\sum_{i\in I}{f_i}z_i \\ s.t.\quad &\sum_{i\in I}{a_{ji}^{k}x_{i}^{k}}\leqslant b_j, \quad \forall j\in J,\forall k\in K \\ &\sum_{k\in K}{x_{i}^{k}}-Mz_i\leqslant 0, \quad \forall i\in I \\ &z_i\in \left\{ 0,1 \right\} ,x_{i}^{k}\geqslant 0, \quad \forall i\in I,\forall k\in K\end{aligned} maxs.t.k∈K∑pk(i∈I∑cixik)−i∈I∑fizii∈I∑ajikxik⩽bj,∀j∈J,∀k∈Kk∈K∑xik−Mzi⩽0,∀i∈Izi∈{
0,1},xik⩾0,∀i∈I,∀k∈K
第2条约束表示,只要有一个场景下, x i k > 0 x_i^k >0 xik>0,则 z i = 1 z_i=1 zi=1。也就是,只要有一种情况下,生产了产品 i ∈ { A , B } i \in \{A, B\} i∈{
A,B},则我们就需要在一开始就要决定生产产品 i ∈ { A , B } i \in \{A, B\} i∈{
A,B},且准备好生产 i ∈ { A , B } i \in \{A, B\} i∈{
A,B}的所有预备材料。
上面的模型虽然包含了两阶段决策:上层决策(tactical level)
和作业层决策(operational level)
。但是模型看上去看是单阶段的模型
。不过上面的写法是没问题的,只是没有体现出两阶段的思想。
Two-stage Stochastic Programming
的更学术的写法
为了体现出两阶段的思想,我们换一种更专业,更学术的写法,学术论文里大家一般也都是按照下面的方法写的。
我们按照事情发生的先后顺序的逻辑来写。
首先,我们需要做一个上层决策(tactical level)
,这个决策即为:要不要生产产品 A A A和产品 B B B。做出的决策必须要使得总的期望净利润
最大化。即
max E p [ h ( z , K ) ] − ∑ i ∈ I f i z i z i ∈ { 0 , 1 } , ∀ i ∈ I \begin{aligned} \max \quad &\mathbb{E}_p\left[ h\left( \mathbf{z},K \right) \right] -\sum_{i\in I}{f_i}z_i \\ &z_i\in \left\{ 0,1 \right\} ,\qquad \forall i\in I \end{aligned} maxEp[h(z,K)]−i∈I∑fizizi∈{
0,1},∀i∈I
这里,符号上可能有些难理解,我在这里详细的解释一下。 E p [ h ( z , K ) ] \mathbb{E}_p\left[ h\left( \mathbf{z},K \right) \right] Ep[h(z,K)],就是在各个场景出现的概率为 p p p时(例如 p = [ 0.2 , 0.2 , 0.2 , 0.2 , 0.2 ] p = [0.2, 0.2, 0.2, 0.2, 0.2] p=[0.2,0.2,0.2,0.2,0.2]),决策变量 z = [ z A , z B ] T \mathbf{z}=[z_A, z_B]^T z=[zA,zB]T的取值确定以后,在所有场景 K K K下的收入的期望值。实际上,就是第二阶段决策产生的收入的期望值,但是现在我们还没有决策第二阶段的生产计划,因此我们先用一个符号去表示第二阶段的期望收入值
。
当我们确定了是否要生产产品 A A A、 B B B的决策 z = [ z A , z B ] T \mathbf{z}=[z_A, z_B]^T z=[zA,zB]T以后,第二阶段,也就是制定生产计划的阶段,我们会面临不确定的情况,也就是面临多种场景 ∀ k ∈ K \forall k \in K ∀k∈K。对于每一种场景 k ∈ K k \in K k∈K,当这种场景
k k k真的发生了的时候,我们需要做出在这种场景下的最优的生产计划。最优的生产计划可以用下面的模型规划出来
注意,之前是
h ( z , K ) h\left( \mathbf{z},K \right) h(z,K),是指在所有的场景
K K K下的所有决策,这里我们固定了某一种场景
k k k,因此变成了
h ( z , k ) h\left( \mathbf{z},k \right) h(z,k)。此时 z \mathbf{z} z已经可以看做是一个给定的值,因为在做第二阶段生产计划的时候,决定生产还是不生产 A A A和 B B B是已经知道的事实。
h ( z , k ) = max x ( k ) ∑ i ∈ I c i x i k s . t . ∑ i ∈ I a j i k x i k ⩽ b j , ∀ j ∈ J , ∀ k ∈ K ∑ k ∈ K ∑ i ∈ I x i k − M z i ⩽ 0 , ∀ i ∈ I x i k ⩾ 0 , ∀ i ∈ I , ∀ k ∈ K \begin{aligned} h\left( \mathbf{z},k \right) &=\underset{\mathbf{x}\left( k \right)}{\max}\,\,\sum_{i\in I}{c_i}x_{i}^{k} \\ s.t.&\quad \sum_{i\in I}{a_{ji}^{k}x_{i}^{k}}\leqslant b_j, \qquad \quad \forall j\in J,\forall k\in K \\ &\quad \sum_{k\in K}{\sum_{i\in I}{x_{i}^{k}}}-Mz_i\leqslant 0, \quad \forall i\in I \\ &\quad x_{i}^{k}\geqslant 0,\quad \qquad \qquad \qquad \forall i\in I,\forall k\in K \end{aligned} h(z,k)s.t.=x(k)maxi∈I∑cixiki∈I∑ajikxik⩽bj,∀j∈J,∀k∈Kk∈K∑i∈I∑xik−Mzi⩽0,∀i∈Ixik⩾0,∀i∈I,∀k∈K
怎么样,这样写,是不是就比较好理解两个阶段的决策上层决策(tactical level)
和作业层决策(operational level)
之间是什么关系了。
我们来汇总一下上面的两阶段模型。
首先,
第一阶段
,作为企业的决策者,我们需要作出是否生产产品 A A A和 B B B的上层决策(tactical level decisions
),使得总的期望净利润
最大化。这一阶段的决策,是和随机场景无关的,无论场景怎么变,这一阶段的决策产生的固定成本都是相同的。模型为
max E p [ h ( z , K ) ] − ∑ i ∈ I f i z i z i ∈ { 0 , 1 } , ∀ i ∈ I \begin{aligned} \max \quad &\mathbb{E}_p\left[ h\left( \mathbf{z},K \right) \right] -\sum_{i\in I}{f_i}z_i \\ &z_i\in \left\{ 0,1 \right\} ,\qquad \forall i\in I \end{aligned} maxEp[h(z,K)]−i∈I∑fizizi∈{ 0,1},∀i∈I
制定好了上层决策之后,真正到了要生产的时候,我们观察到某种可能的场景 k ∈ K k \in K k∈K发生了,我们需要作出该场景下的最优生产决策,即:
第二阶段
的决策:亦即下层决策(operational level decisions
),对于场景 k ∈ K k \in K k∈K,我们有
h ( z , k ) = max x ( k ) ∑ i ∈ I c i x i k s . t . ∑ i ∈ I a j i k x i k ⩽ b j , ∀ j ∈ J , ∀ k ∈ K ∑ k ∈ K ∑ i ∈ I x i k − M z i ⩽ 0 , ∀ i ∈ I x i k ⩾ 0 , ∀ i ∈ I , ∀ k ∈ K \begin{aligned} h\left( \mathbf{z},k \right) &=\underset{\mathbf{x}\left( k \right)}{\max}\,\,\sum_{i\in I}{c_i}x_{i}^{k} \\ s.t.&\quad \sum_{i\in I}{a_{ji}^{k}x_{i}^{k}}\leqslant b_j, \quad \forall j\in J,\forall k\in K \\ &\quad \sum_{k\in K}{\sum_{i\in I}{x_{i}^{k}}}-Mz_i\leqslant 0, \qquad \forall i\in I \\ &\quad x_{i}^{k}\geqslant 0,\quad \quad \quad \forall i\in I,\forall k\in K \end{aligned} h(z,k)s.t.=x(k)maxi∈I∑cixiki∈I∑ajikxik⩽bj,∀j∈J,∀k∈Kk∈K∑i∈I∑xik−Mzi⩽0,∀i∈Ixik⩾0,∀i∈I,∀k∈K
怎么样,这么写是不是高大上一些。以后小伙伴们写论文,大概都需要这么写的。这样比较专业一些。如果按照之前的那种写法,可能会被喷不专业。哈哈哈。
两阶段的写法比较容易理解两阶段决策的逻辑,但在真正求解的时候,是和前面那种写法是完全一样的。为了方便大家查看,我再抄一遍过来。
合并书写的方式
max ∑ k ∈ K p k ( ∑ i ∈ I c i x i k ) − ∑ i ∈ I f i z i s . t . ∑ i ∈ I a j i k x i k ⩽ b j , ∀ j ∈ J , ∀ k ∈ K ∑ k ∈ K x i k − M z i ⩽ 0 , ∀ i ∈ I z i ∈ { 0 , 1 } , x i k ⩾ 0 , ∀ i ∈ I , ∀ k ∈ K \begin{aligned} \max \,\,&\sum_{k\in K}{p_k}\left( \sum_{i\in I}{c_i}x_{i}^{k} \right) -\sum_{i\in I}{f_i}z_i \\ s.t.\quad &\sum_{i\in I}{a_{ji}^{k}x_{i}^{k}}\leqslant b_j, \quad \forall j\in J,\forall k\in K \\ &\sum_{k\in K}{x_{i}^{k}}-Mz_i\leqslant 0, \quad \forall i\in I \\ &z_i\in \left\{ 0,1 \right\} ,x_{i}^{k}\geqslant 0, \quad \forall i\in I,\forall k\in K\end{aligned} maxs.t.k∈K∑pk(i∈I∑cixik)−i∈I∑fizii∈I∑ajikxik⩽bj,∀j∈J,∀k∈Kk∈K∑xik−Mzi⩽0,∀i∈Izi∈{ 0,1},xik⩾0,∀i∈I,∀k∈K
我们真正进行求解的时候,还是用这种写法去求解就可以。还是那句话,求解器根本不知道你的决策变量哪个是上层决策,哪个是下层决策。当你把模型丢给求解器,人家直接就求解了,管你什么一阶段二阶段呢,哈哈哈。除非你自己根据一阶段二阶段设计了专门的算法,先固定哪个,后做哪个。比如说,你用Benders Decomposition,把第一阶段的z作为复杂变量,把第二阶段的x作为简单变量,那就另当别论。
至于随机规划更为复杂的例子,大家可以去读我的母系,工业工程系毕业的黄一潇师兄2017年发在TRB上的论文Time-dependent vehicle routing problem with path flexibility
。
下面我们继续用Python
调用Gurobi
来求解上述Two-stage Stochastic Programming
。
Python
调用Gurobi
求解Two-stage Stochastic Programming
我们沿用前一篇文章的所有关于均值和方差的假设,即
E ( a ~ 11 ) = μ 11 = 1 , σ 11 = 0.2 E ( a ~ 12 ) = μ 12 = 2 , σ 12 = 0.2 E ( a ~ 21 ) = μ 21 = 4 , σ 21 = 0.2 E ( a ~ 32 ) = μ 32 = 4 , σ 32 = 0.2 \begin{aligned} \mathbb{E}(\tilde{a}_{11}) = \mu_{11} = 1, \quad \sigma_{11} = 0.2 \\ \mathbb{E}(\tilde{a}_{12}) = \mu_{12} = 2, \quad \sigma_{12} = 0.2 \\ \mathbb{E}(\tilde{a}_{21}) = \mu_{21} = 4, \quad \sigma_{21} = 0.2 \\ \mathbb{E}(\tilde{a}_{32}) = \mu_{32} = 4, \quad \sigma_{32} = 0.2 \end{aligned} E(a~11)=μ11=1,σ11=0.2E(a~12)=μ12=2,σ12=0.2E(a~21)=μ21=4,σ21=0.2E(a~32)=μ32=4,σ32=0.2
我们设置 f A = 6 , f B = 4 f_A = 6, f_B=4 fA=6,fB=4。
我们用Python调用Gurobi
对其进行求解。
为了方便与只有操作层决策的版本进行对比,我们首先将不确定参数固定住,即保证二者使用同一组约束系数。
scenario_num = 5
# generate parameters under scenarios
a_11 = np.random.normal(1, 0.2, scenario_num) # mu, sigma, sample_number
a_12 = np.random.normal(2, 0.2, scenario_num)
a_21 = np.random.normal(4, 0.2, scenario_num)
a_32 = np.random.normal(4, 0.2, scenario_num)
print('a_11:', a_11)
print('a_12:', a_12)
print('a_21:', a_21)
print('a_32:', a_32)
产生的随机输入参数为
a_11: [0.90039351 1.38590641 1.18988416 1.01751025 0.7549129 ]
a_12: [2.1688726 1.79995693 1.69104578 2.23760596 2.06338852]
a_21: [4.18417176 4.06374553 4.17136612 3.86979488 3.79315143]
a_32: [4.1363189 3.83931807 3.86209004 3.9088935 4.00349583]
Two-stage Stochastic Programming
from gurobipy import *
sto_model = Model('Stochastic Programming')
prob = [0.2, 0.2, 0.2, 0.2, 0.2]
# prob = [0.1, 0.2, 0.3, 0.15, 0.25]
profit = [2, 3]
fix_cost = [6, 4]
b = [8, 16, 12]
big_M = 1000
x_sto = {
}
z = {
}
for i in range(2):
# creat variables
z[i] = sto_model.addVar(lb = 0, ub = 1, vtype = GRB.BINARY, name = 'z_' + str(i) + '_' + str(i))
for s in range(scenario_num):
x_sto[i, s] = sto_model.addVar(lb = 0, ub = GRB.INFINITY, vtype = GRB.CONTINUOUS, name = 'x_' + str(i) + '_' + str(s))
# set objective
obj = LinExpr(0)
for key in x_sto.keys():
product_ID = key[0]
scenario_ID = key[1]
obj.addTerms(prob[scenario_ID] * profit[product_ID], x_sto[key])
for key in z.keys():
obj.addTerms(-fix_cost[key], z[key])
sto_model.setObjective(obj, GRB.MAXIMIZE)
# add constraints:
# constraints 1-1
for s in range(scenario_num):
lhs = LinExpr(0)
lhs.addTerms(a_11[s], x_sto[0, s])
lhs.addTerms(a_12[s], x_sto[1, s])
sto_model.addConstr(lhs <= b[0], name = 'cons_' + str(0))
# constraints 1-2
for s in range(scenario_num):
lhs = LinExpr(0)
lhs.addTerms(a_21[s], x_sto[0, s])
sto_model.addConstr(lhs <= b[1], name = 'cons_' + str(1))
# constraints 1-3
for s in range(scenario_num):
lhs = LinExpr(0)
lhs.addTerms(a_32[s], x_sto[1, s])
sto_model.addConstr(lhs <= b[2], name = 'cons_' + str(2))
# constraints 2
for i in range(2):
lhs = LinExpr(0)
for s in range(scenario_num):
lhs.addTerms(1, x_sto[i, s])
sto_model.addConstr(lhs <= big_M * z[i])
# solve
sto_model.optimize()
print('Obj = ', sto_model.ObjVal)
print('\n\n ------ tactical level decision ------- ')
for key in z.keys():
print(z[key].varName, ' = ', z[key].x)
print('\n\n ------ operational level decision ------- ')
for s in range(scenario_num):
print(' ------ scenario ', s, ' ------- ')
for i in range(2):
print(x_sto[i,s].varName, ' = ', x_sto[i,s].x)
print('\n\n')
求解结果为
-- Obj = 5.120668471999894
------ tactical level decision -------
z_0_0 = 0.0
z_1_1 = 1.0
------ operational level decision -------
------ scenario 0 -------
x_0_0 = 0.0
x_1_0 = 2.901130275374204
------ scenario 1 -------
x_0_1 = 0.0
x_1_1 = 3.1255550569323436
------ scenario 2 -------
x_0_2 = 0.0
x_1_2 = 3.107125898642543
------ scenario 3 -------
x_0_3 = 0.0
x_1_3 = 3.069922473497831
------ scenario 4 -------
x_0_4 = 0.0
x_1_4 = 2.997380415552898
可以看到,我们最终是决定,不生产产品
A A A,只生产产品 B B B,因为产品 A A A的固定成本高( f A = 6 f_A=6 fA=6),但是利润比较低(为2)。而产品 B B B的固定成本低( f A = 4 f_A=4 fA=4),而且利润高(为3)。
接下来,我们就对比一下之前那种只有操作层的版本。
Single stage version
scenario_num = 5
sto_model = Model('Stochastic Programming')
prob = [0.2, 0.2, 0.2, 0.2, 0.2]
# prob = [0.1, 0.2, 0.3, 0.15, 0.25]
profit = [2, 3]
b = [8, 16, 12]
# generate parameters under scenarios
# a_11 = np.random.normal(1, 0.2, scenario_num) # mu, sigma, sample_number
# a_12 = np.random.normal(2, 0.2, scenario_num)
# a_21 = np.random.normal(4, 0.2, scenario_num)
# a_32 = np.random.normal(4, 0.2, scenario_num)
x_sto = {
}
for i in range(2):
# creat variables
for s in range(scenario_num):
x_sto[i, s] = sto_model.addVar(lb = 0, ub = GRB.INFINITY, vtype = GRB.CONTINUOUS, name = 'x_' + str(i) + '_' + str(s))
# set objective
obj = LinExpr(0)
for key in x_sto.keys():
product_ID = key[0]
scenario_ID = key[1]
obj.addTerms(prob[scenario_ID] * profit[product_ID], x_sto[key])
sto_model.setObjective(obj, GRB.MAXIMIZE)
# add constraints:
# constraints 1
for s in range(scenario_num):
lhs = LinExpr(0)
lhs.addTerms(a_11[s], x_sto[0, s])
lhs.addTerms(a_12[s], x_sto[1, s])
sto_model.addConstr(lhs <= b[0], name = 'cons_' + str(0))
# constraints 2
for s in range(scenario_num):
lhs = LinExpr(0)
lhs.addTerms(a_21[s], x_sto[0, s])
sto_model.addConstr(lhs <= b[1], name = 'cons_' + str(1))
# constraints 3
for s in range(scenario_num):
lhs = LinExpr(0)
lhs.addTerms(a_32[s], x_sto[1, s])
sto_model.addConstr(lhs <= b[2], name = 'cons_' + str(2))
# solve
sto_model.optimize()
print('Obj = ', sto_model.ObjVal)
# for key in x_sto.keys():
# print(x_sto[key].varName, ' = ', x_sto[key].x)
for s in range(scenario_num):
print(' ------ scenario ', s, ' ------- ')
for i in range(2):
print(x_sto[i,s].varName, ' = ', x_sto[i,s].x)
print('\n\n')
求解结果如下
--- Obj = 13.896545328523917
------ scenario 0 -------
x_0_0 = 3.8239347951176703
x_1_0 = 2.101070361744041
------ scenario 1 -------
x_0_1 = 1.71305615957035
x_1_1 = 3.1255550569323436
------ scenario 2 -------
x_0_2 = 2.307542152652112
x_1_2 = 3.107125898642543
------ scenario 3 -------
x_0_3 = 4.134586067378273
x_1_3 = 1.6951225436724826
------ scenario 4 -------
x_0_4 = 4.218128458180001
x_1_4 = 2.333869931282845
是吧,这两个的结果还是完全不一样的吧。
此时,由于上层决策变量 z i , ∀ i ∈ I z_i, \forall i \in I zi,∀i∈I和下层决策变量 x i k , ∀ i ∈ I , k ∈ K x_i^k, \forall i \in I, k \in K xik,∀i∈I,k∈K是有耦合关系的,是一揽子
决策。场景和场景之间也是有一些关系的,比如在场景1下,我们生产了产品 A A A,即 x A 1 > 0 x_A^1 >0 xA1>0,那么 z A z_A zA就一定为1,因此在场景2下,也许就会生产 x A 2 x_A^2 xA2也许就大于0了。但是,也许你单独求解多个模型的时候, x A 2 = 0 x_A^2 = 0 xA2=0。这种情形是有可能会出现的。在这种情况下,单独求解每个场景下的模型,然后加权求和一下,就和随机规划的模型的结果是不等价的。
所以说,随机规划肯定不是单独求解多个线性规划,然后加权求和一下。它是有自己独特的强大功能在的。
好啦,这篇文章就是完整的给大家展示了两阶段随机规划
的一个小例子,从构建决策,到建模,到代码实现。相信大家读完这篇文章,应该对随机规划有了一个非常清楚的初步认识。之后如果想要继续深入,欢迎阅读下面的两本书。或者持续关注我们的公众号。
参考文献
[1]: Shapiro, A and Ruszczynski, A, Handbooks in operations research and management science: Vol. 10. Stochastic programming
, 2003
[2]: Birge, John R., and Francois Louveaux. Introduction to stochastic programming
. Springer Science & Business Media, 2011.
[3]: Huang, Y., Zhao, L., Van Woensel, T., & Gross, J. P. (2017). Time-dependent vehicle routing problem with path flexibility. Transportation Research Part B: Methodological, 95, 169-195.
作者:刘兴禄,清华大学,清华伯克利深圳学院,博士在读
联系方式:[email protected]
博客主页:https://blog.csdn.net/HsinglukLiu/article/details/107848461
OlittleRer
运小筹公众号是致力于分享运筹优化(LP、MIP、NLP、随机规划、鲁棒优化)、凸优化、强化学习等研究领域的内容以及涉及到的算法的代码实现。编程语言和工具包括Java、Python、Matlab、CPLEX、Gurobi、SCIP 等。
关注我们: 运筹小公众号
也欢迎广大同行投稿!欢迎大家关注我们的公众号“运小筹”以及粉丝群!
微信群:
QQ群:
QQ群里有我们共享的一些书籍、论文、算例等资料,欢迎大家加入。