初识随机规划 (2):两阶段随机规划(一个详细的例子)

初识随机规划 (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 AB的量,则该问题的数学模型为
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+2x284x1164x212x1,x20

但是上面的文章只是让读者直观的感受一下随机规划究竟是什么,文中的随机规划模型如下:

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.kKpk(c1x1k+c2x2k)a11kx1k+a12kx2k8,kKa21kx1k16,kKa32kx212,kKx1k,x2k0,kK
其紧凑形式为
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.kKpkiIcixikiIajikxikbj,jJ,kKxik0,iI,kK
其中 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 jJ种资源被第 i ∈ I i \in I iI种商品在第 k ∈ K k \in K kK个场景下需要的量。

细心的读者会发现,上面的模型,其实可以等价为:分别求解 K K K个线性规划,然后将 K K K个单独的线性规划的目标值加权求和一下。原因是这 K K K个场景下的模型没有任何联系,是相互独立的。

难道说随机规划就是很多个确定性线性规划简单的分别求解一下,然后把目标函数加和一下这么简单吗?当然不是。

3层决策:战略层(strategic)战术层(tactical)作业层(operational)

一般情况下,随机规划都是有多个阶段的决策的。一般来讲,决策有三个层次:
战略层(strategic)战术层(tactical)作业层(operational)。从strategictactical再到operational,决策的周期越来越短,决策的事情越来越具体化。随机规划中,比较常见的是两个阶段的决策two-stage decision,因此,读论文的时候,经常看到关于随机规划的文章是把问题建模为一个two-stage mixed integer programming的模型求解的。

所谓的two-stage,一般可以认为包含两种决策,一种是比较长期的 ,上层的决策,比如服务网络怎么搭建,物流网络怎么搭建,基础设施怎么搭建等,这种决策会影响企业较长时间的发展,我们称其为战略层决策(strategic level),有的决策也许没有到战略层的级别,可以称为战术层决策(tactical level)。第二种决策就是非常具体化的,比如生产计划,采购计划,VRP的路径规划等,都是非常具体、细节的决策,这一类决策对企业的发展影响周期较短,可以称为作业层决策(operational level)。为了方便统一,本文中我们统一使用tactical leveloperational 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.kKpk(iIcixik)iIfiziiIajikxikbj,jJ,kKkKxikMzi0,iIzi{ 0,1},xik0,iI,kK
第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)]iIfizizi{ 0,1},iI
这里,符号上可能有些难理解,我在这里详细的解释一下。 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 kK。对于每一种场景 k ∈ K k \in K kK当这种场景 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)maxiIcixikiIajikxikbj,jJ,kKkKiIxikMzi0,iIxik0,iI,kK

怎么样,这样写,是不是就比较好理解两个阶段的决策上层决策(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)]iIfizizi{ 0,1},iI
制定好了上层决策之后,真正到了要生产的时候,我们观察到某种可能的场景 k ∈ K k \in K kK发生了,我们需要作出该场景下的最优生产决策,即:
第二阶段的决策:亦即下层决策(operational level decisions),对于场景 k ∈ K k \in K kK,我们有
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)maxiIcixikiIajikxikbj,jJ,kKkKiIxikMzi0,iIxik0,iI,kK
怎么样,这么写是不是高大上一些。以后小伙伴们写论文,大概都需要这么写的。这样比较专业一些。如果按照之前的那种写法,可能会被喷不专业。哈哈哈。

两阶段的写法比较容易理解两阶段决策的逻辑,但在真正求解的时候,是和前面那种写法是完全一样的。为了方便大家查看,我再抄一遍过来。

合并书写的方式 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.kKpk(iIcixik)iIfiziiIajikxikbj,jJ,kKkKxikMzi0,iIzi{ 0,1},xik0,iI,kK
我们真正进行求解的时候,还是用这种写法去求解就可以。还是那句话,求解器根本不知道你的决策变量哪个是上层决策,哪个是下层决策。当你把模型丢给求解器,人家直接就求解了,管你什么一阶段二阶段呢,哈哈哈。除非你自己根据一阶段二阶段设计了专门的算法,先固定哪个,后做哪个。比如说,你用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,iI和下层决策变量 x i k , ∀ i ∈ I , k ∈ K x_i^k, \forall i \in I, k \in K xik,iI,kK是有耦合关系的,是一揽子决策。场景和场景之间也是有一些关系的,比如在场景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群里有我们共享的一些书籍、论文、算例等资料,欢迎大家加入。

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/HsinglukLiu/article/details/112756898