列生成在求解大型线性规划时往往都表现良好,在很多时候,一个变量很多的线性规划问题中,往往很多的列是用不到的,或者说,它们并不影响最优解的求解。列生成正是基于这样一种思想,从有限列出发,每次在求解限制问题后,判断是否达到最优,若达到,则返回结果,否则,添加新的列,继续求解,直到求出最优解为止。
本文将从列生成原理的证明,基于一维cutting问题的步骤说明和python代码实现来详细讲解列生成算法。同时比较该问题的几种模型在gurobi求解下的速度和解的质量。
一维cutting问题可以描述为:假设有无限多的长度为 L L L的木板和 n n n种类型的需求,第 i i i ( i i i=1,…, n n n)类型需求的尺寸为 l i l_i li,需求数量为 d i d_i di,设 M = { 1 , 2 , . . . , m } M=\{1,2,...,m\} M={1,2,...,m}, N = { 1 , 2 , . . . , n } N=\{1,2,...,n\} N={1,2,...,n}试求如何选择下料方式,在满足需求的情况下,使得使用的木板(board)数目最少?
在进一步说明列生成之前,我们先看一维cutting问题的相关模型描述比较不同模型求解的效率。
该问题的模型建立是简单的,在建立第一个模型之前,我们先定义相关的决策变量:
y j = 1 y_j=1 yj=1 如果第 j j j个木板被使用,否则 y j = 0 y_j=0 yj=0, y j ∈ { 0 , 1 } y_j \in \{0,1\} yj∈{0,1};
x i j x_{ij} xij 代表来自第 j j j个木板的第 i i i中类型需求的数量, x i j ∈ N x_{ij}\in\N xij∈N.
m o d e l model model 1 : 1: 1:
m i n ∑ j ∈ M y j min\sum\limits_{j\in M}y_j minj∈M∑yj (1)
s.t. I y j ≥ x i j , i ∈ N , j ∈ M Iy_j\ge x_{ij},i\in N,j\in M Iyj≥xij,i∈N,j∈M (2)
∑ j ∈ M l i x i j ≤ L , j ∈ M \sum\limits_{j\in M}l_ix_{ij}\le L,j\in M j∈M∑lixij≤L,j∈M (3)
∑ i ∈ N x i j ≥ d i , i ∈ N \sum\limits_{i\in N}x_{ij}\ge d_i, i\in N i∈N∑xij≥di,i∈N (4)
公式(1)定义了问题的目标,即使用的board数量最少,显然, I I I是一个充分大的数,且 m = ∑ i ∈ N d i m=\sum\limits_{i\in N}d_i m=i∈N∑di显然是该问题的一个上界。当然, m m m取值越大意味着更多的决策变量,而实际上,任何一种启发式方法或者可行方案下使用的board数目都是该问题的一个可行上界。而在该问题中,我们取简单上界 m = ∑ i ∈ N ⌈ d i ⌊ L l i ⌋ ⌉ m=\sum\limits_{i \in N}{\left\lceil \frac{d_i}{\left\lfloor \frac{L}{l_i} \right\rfloor} \right\rceil} m=i∈N∑⌈⌊liL⌋di⌉。公式(2)限制了当 y j = 0 y_j=0 yj=0时,即第 j j j个board不被使用时, x i j = 0 x_{ij}=0 xij=0。其中, I I I是一个充分大的常数,因此当 y j = 0 y_j=0 yj=0时,对 x i j x_{ij} xij不作约束。公式(3)限制了从board j j j上切下的物品尺寸总和不能超过baord的总长 L L L。公式(4)则表示相应的需求约束。
我们通过一个实例的求解来说明下这个模型的求解效率,我们设 L = 110 L=110 L=110,考虑5种不同的需求如下表所示。
尺寸 l i l_i li | 20 | 45 | 50 | 55 | 75 |
---|---|---|---|---|---|
需求 d i d_i di | 48 | 35 | 24 | 10 | 8 |
将数据代入模型1,python编程,调用gurobi求解得到的结果为 O P T = 47 OPT=47 OPT=47,时间 r u n t i m e = 30.3 s runtime=30.3s runtime=30.3s.
进一步地,我们用该模型的松弛形式,即松弛 0 ≤ y j ≤ 1 0 \le y_j \le 1 0≤yj≤1 和 x i j ≥ 0 x_{ij} \ge 0 xij≥0 代替model 1中的决策变量约束来估计该问题的下界。得到 O P T = 0.048 OPT=0.048 OPT=0.048,时间 r u n t i m e = 0.009 s runtime=0.009s runtime=0.009s。下界的意义之一在于:随着求解规模的增大,当一个模型的求解时间是呈指数增长时,我们往往会选择一些启发式算法或智能算法来求解相应的问题。因此,此时可以用下界来评价一个算法的有效性(effectiveness),而当你提供一个足够好的下界,而你启发式算法的求解结果和下界差距足够小时,即可以说明你的算法能求解出足够好的近似优解。显然,第一个模型的松弛模型的下界足够差。因为我们可以很简单地得到该问题的一个连续下界
l o w e r b o u n d = ⌈ ∑ i ∈ N d i l i L ⌉ ( 5 ) lowerbound=\left\lceil \frac{\sum\limits_{i \in N}{d_il_i}}{L} \right\rceil (5) lowerbound=⎢⎢⎢⎡Li∈N∑dili⎥⎥⎥⎤(5).
且通过公式(5)我们可以求得的该实例的下界为45.
很多时候,模型建立的不同可能会对求解效率产生很大的影响,基于model 1,建立新的cutting模型,如下所示。
m o d e l model model 2 : 2: 2:
m i n ∑ j ∈ M y j min\sum\limits_{j\in M}y_j minj∈M∑yj (6)
s.t. ∑ j ∈ M l i x i j ≤ L y j , j ∈ M \sum\limits_{j\in M}l_ix_{ij}\le Ly_j,j\in M j∈M∑lixij≤Lyj,j∈M (7)
∑ i ∈ N x i j ≥ d i , i ∈ N \sum\limits_{i\in N}x_{ij}\ge d_i, i\in N i∈N∑xij≥di,i∈N (8)
该模型进行了简单的调整,经过求解器求解,我们可以得到 O P T = 47 OPT=47 OPT=47,时间 r u n t i m e = 8 s runtime=8s runtime=8s. 而对应的松弛模型 r u n t i m e = 0.003 s , O P T = 44.41 runtime=0.003s,OPT=44.41 runtime=0.003s,OPT=44.41。实际上,我们可以发现,该线性模型求解得结果和公式(5)求解结果是相同的,在其对应的松弛问题中,我们可以定义 y j y_j yj为board j j j 使用的比例 0 ≤ y j ≤ 1 0 \le y_j \le 1 0≤yj≤1,从这个角度上来看,它和公式(5)的原理是一致的。
同时,从这里我们可以看出,建立不同的数学模型,可能会极大地影响模型的求解效率。
首先考虑一个目标为极小值的线性规划模型 P P P 如下所示
min z = c T x z=c^Tx z=cTx
( P ) (P) (P) { A x ≥ b x ≥ 0 ( 9 ) \left\{ \begin{array}{l} Ax \ge b \\ x \ge 0 \end{array} \right. (9) {Ax≥bx≥0(9)
其中, A A A为 m × n m \times n m×n的二维矩阵, c , x c,x c,x 均为 n × 1 n \times 1 n×1的列向量, b b b为 m × 1 m \times 1 m×1的列向量。设 A ~ \tilde A A~为 m × n ′ m \times n' m×n′的二维矩阵,且 n ′ ≤ n n' \le n n′≤n, A ~ \tilde A A~中列向量为 A A A中列向量的子集,则我们可以定义相应的限制问题 R P RP RP如下所示
min z = c ′ T x ′ z=c'^Tx' z=c′Tx′
( R P ) (RP) (RP) { A ~ x ′ ≥ b x ′ ≥ 0 ( 10 ) \left\{ \begin{array}{l} \tilde Ax' \ge b \\ x' \ge 0 \end{array} \right. (10) {A~x′≥bx′≥0(10)
模型 ( P ) (P) (P)和模型 ( R P ) (RP) (RP)的对偶问题 D P DP DP和 D R P DRP DRP可以表示如下:
max w = y T b w=y^Tb w=yTb
( D P ) (DP) (DP) { A T y ≤ c y ≥ 0 ( 11 ) \left\{ \begin{array}{l} A^Ty \le c \\ y \ge 0 \end{array} \right. (11) {ATy≤cy≥0(11)
max w = y T b w=y^Tb w=yTb
( D R P ) (DRP) (DRP) { A ~ T y ≤ c ′ y ≥ 0 ( 12 ) \left\{ \begin{array}{l} \tilde A^Ty \le c' \\ y \ge 0 \end{array} \right. (12) {A~Ty≤c′y≥0(12)
由强对偶性可知,如果 ( R P ) (RP) (RP)的最优解是 P P P的最优解,当且仅当 D R P DRP DRP的最优解是 D P DP DP的最优解。此时,由于 D R P DRP DRP的行(即约束)是 R P RP RP的行的子集,从另一方面理解, R P RP RP问题的解空间是 D R P DRP DRP的解空间的子集。因此,若 D R P DRP DRP问题的最优解满足 D P DP DP的所有约束,则 D R P DRP DRP的最优解即为 R P RP RP问题的最优解。
综上所述,当求出 R P RP RP的最优解 x ′ ∗ x'^* x′∗时,要判断其是否是 P P P的最优解 x ∗ x^* x∗,则需要判断 D R P DRP DRP模型最优解 y ∗ y^* y∗是否满足 D P DP DP中的所有约束。设 a i a_i ai为系数矩阵 A A A中的列向量,则模型 D P DP DP可以重新写为:
max w = y T b w=y^Tb w=yTb
( D P ) (DP) (DP) { a i T y ≤ c i , i = 1 , 2 , . . . , n y ≥ 0 ( 13 ) \left\{ \begin{array}{l} a_i^Ty \le c_i,i=1,2,...,n \\ y \ge 0 \end{array} \right. (13) {aiTy≤ci,i=1,2,...,ny≥0(13)
因此,若 y ∗ y^* y∗是 D P DP DP的最优解,即满足 D P DP DP的所有约束,则应该 a i T y ∗ ≤ c i , ∀ i = 1 , 2 , . . . , n a_i^Ty^* \le c_i,\forall i=1,2,...,n aiTy∗≤ci,∀i=1,2,...,n。判断 y ∗ y^* y∗是否满足 ( 13 ) (13) (13)中的所有约束,等价于求解 ( 14 ) (14) (14)
( 14 ) (14) (14)
min z = c i − a i T y ∗ z=c_i-a_i^Ty^* z=ci−aiTy∗
a i ∈ A a_i \in A ai∈A
如果我们求解该问题 w ∗ ≥ 0 w^* \ge 0 w∗≥0,即 ∀ i , a i T y ∗ ≤ c i \forall i, a_i^Ty^* \le c_i ∀i,aiTy∗≤ci,此时 y ∗ y^* y∗即为 D P DP DP的最优解。若 w ∗ < 0 w^* \lt 0 w∗<0,则应添加相应的 a i ∗ a_{i^*} ai∗,继续求解,直到 w ∗ ≥ 0 w^* \ge 0 w∗≥0为止。
该部分建立该问题的集合覆盖(set-covering model),如下所示:
min ∑ j ∈ P x j \sum\limits_{j \in P}{x_j} j∈P∑xj
{ ∑ j ∈ P n j i x j ≥ d i , i ∈ N x j ∈ N , j ∈ P ( 15 ) \left\{ \begin{array}{l} \sum\limits_{j \in P}n_{ji}x_j \ge d_i,i \in N\\ x_j \in N,j \in P \end{array} \right. (15) {j∈P∑njixj≥di,i∈Nxj∈N,j∈P(15)
其中, P P P为所有可行的切割方式 ( p a t t e r n ) (pattern) (pattern), n j i n_{ji} nji为pattern j j j中第 i i i中类型需求的数量, x j x_j xj为pattern j j j的使用数目。
但是事实上,实现罗列出来所有可行的pattern 是不可行的,而往往在最优解中使用到的pattern的数量一般是远远小于总的pattern数量的。因此,借鉴列生成的思路,从有限列(即限制问题 R P RP RP)出发,经过不断的迭代,每次判断当前有限列是否达到最优,若达到,返回当前最优解;否则,添加列,直至达到最优为止。其流程图如下所示。
本部分通过一维cutting问题的实例详细说明该问题的求解步骤。
设初始集合覆盖模型系数矩阵为:
n = [ 5 0 0 0 0 0 2 0 0 0 0 0 2 0 0 0 0 0 2 0 0 0 0 0 1 ] n = \left[ {\begin{array}{l} 5&0&0&0&0 \\ 0&2&0&0&0 \\ 0&0&2&0&0 \\ 0&0&0&2&0 \\ 0&0&0&0&1 \\ \end{array}} \right] n=⎣⎢⎢⎢⎢⎡5000002000002000002000001⎦⎥⎥⎥⎥⎤
如第1列 n 1 = ( 5 , 0 , 0 , 0 , 0 ) T n_1=(5,0,0,0,0)^T n1=(5,0,0,0,0)T代表的切割方式表示第一种类型需求为5,其余为0,因为 ⌊ L l 1 ⌋ = 5 \left\lfloor \frac{L}{l_1} \right\rfloor = 5 ⌊l1L⌋=5,其余列以此类推。
事实上,建立一个这样的初始求解系数矩阵,能保证问题始终存在可行解。
i t e r iter iter 1:求解限制问题 R P RP RP, x ∗ = ( 9.6 , 17.5 , 12 , 5 , 8 ) T x^*=(9.6, 17.5, 12, 5, 8)^T x∗=(9.6,17.5,12,5,8)T,对应的对偶变量 π = ( 0.2 , 0.5 , 0.5 , 0.5 , 1.0 ) T \pi=(0.2, 0.5, 0.5, 0.5, 1.0)^T π=(0.2,0.5,0.5,0.5,1.0)T,求解定价子问题:
min z = 1 − y T π z=1-y^T\pi z=1−yTπ
{ ∑ i ∈ N y i l j ≤ L y i ∈ N \left\{ \begin{array}{l} \sum\limits_{i \in N}y_il_j \le L\\ y_i \in N \end{array} \right. {i∈N∑yilj≤Lyi∈N
y i y_i yi表示在该可行的patern中第 i i i中类型需求的数目,则 y ∗ = ( 1 , 2 , 0 , 0 , 0 ) T y^*=(1,2,0,0,0)^T y∗=(1,2,0,0,0)T, z ∗ = − 0.2 < 0 z^*=-0.2 \lt 0 z∗=−0.2<0,因此添加 ( 1 , 2 , 0 , 0 , 0 ) T (1,2,0,0,0)^T (1,2,0,0,0)T到原问题系数列,继续求解;
i t e r iter iter 2: 求解新的 R P RP RP问题, x ∗ = ( 6.1 , 0 , 12 , 5 , 8 , 17.5 ) T x^*=(6.1, 0,12, 5, 8, 17.5)^T x∗=(6.1,0,12,5,8,17.5)T,对应的对偶变量 π = ( 0.2 , 0.4 , 0.5 , 0.5 , 1.0 ) T \pi=(0.2, 0.4, 0.5, 0.5, 1.0)^T π=(0.2,0.4,0.5,0.5,1.0)T,求解定价子问题, y ∗ = ( 1 , 0 , 0 , 0 , 1 ) T y^*=(1,0,0,0,1)^T y∗=(1,0,0,0,1)T, z ∗ = − 0.2 < 0 z^*=-0.2 \lt 0 z∗=−0.2<0,因此添加 ( 1 , 0 , 0 , 0 , 1 ) T (1,0,0,0,1)^T (1,0,0,0,1)T到原问题系数列,继续求解;
i t e r iter iter 3: 求解新的 R P RP RP问题, x ∗ = ( 4.5 , 0 , 12 , 5 , 0 , 17.5 , 8 ) T x^*=(4.5, 0,12, 5, 0, 17.5, 8)^T x∗=(4.5,0,12,5,0,17.5,8)T,对应的对偶变量 π = ( 0.2 , 0.4 , 0.5 , 0.5 , 0.8 ) T \pi=(0.2, 0.4, 0.5, 0.5, 0.8)^T π=(0.2,0.4,0.5,0.5,0.8)T,求解定价子问题, y ∗ = ( 3 , 0 , 1 , 0 , 0 ) T y^*=(3,0,1,0,0)^T y∗=(3,0,1,0,0)T, z ∗ = − 0.1 < 0 z^*=-0.1 \lt 0 z∗=−0.1<0,因此添加 ( 3 , 0 , 1 , 0 , 0 ) T (3,0,1,0,0)^T (3,0,1,0,0)T到原问题系数列,继续求解;
i t e r iter iter 3: 求解新的 R P RP RP问题, x ∗ = ( 0 , 0 , 8.25 , 5 , 0 , 17.5 , 8 , 7.5 ) T x^*=(0, 0, 8.25, 5, 0, 17.5, 8, 7.5)^T x∗=(0,0,8.25,5,0,17.5,8,7.5)T,对应的定价子问题 z ∗ > 0 z^* \gt 0 z∗>0,结束循环,输入最优解46.25;
由上述问题可以看到,对列生成求解的下界向上取整,其值和最优解相等。因为,集合覆盖模型往往能获得一个更好的下界,因为一般来说,集合覆盖模型的可行解是原问题的可行解,而原问题的可行解不一定是结合覆盖模型和可行解。
本博客所有涉及模型实现和列生成算法为python调用gurobi编写,相关代码链接可见 https://github.com/shaoxiang-zheng/operation-algorithm.git