为了真正理解单纯形算法的具体步骤,我查阅了许多文章,不过大部分文章会直接叙述单纯形表的操作,对理解算法帮助有限;也有一些文章直接从线性代数上的原理开始叙述,对于非专业人士又太难了。所幸我找到了一个挺不错的在线课程,感觉是我看过叙述单纯形算法最明了直观的资料了,下面的内容主要就是对这个课程的翻译
我们直接就用个例子来叙述算法。还是上一篇的例子:
m a x Z = 40 x 1 + 50 x 2 s . t . x 1 + 2 x 2 ≤ 40 4 x 1 + 3 x 2 ≤ 120 x 1 , x 2 ≥ 0 \begin{aligned} max\quad& Z=40x_1+50x_2\\ s.t.\quad& x_1+2x_2\leq40 \\ & 4x_1+3x_2\leq120 \\ & x_1,x_2\geq0 \end{aligned} maxs.t.Z=40x1+50x2x1+2x2≤404x1+3x2≤120x1,x2≥0
把它松弛化:
m a x Z = 40 x 1 + 50 x 2 s . t . x 1 + 2 x 2 + s 1 = 40 4 x 1 + 3 x 2 + s 2 = 120 x 1 , x 2 , s 1 , s 2 ≥ 0 \begin{aligned} max\quad& Z=40x_1+50x_2\\ s.t.\quad& x_1+2x_2+s_1=40 \\ & 4x_1+3x_2+s_2=120 \\ & x_1,x_2,s_1,s_2\geq0 \end{aligned} maxs.t.Z=40x1+50x2x1+2x2+s1=404x1+3x2+s2=120x1,x2,s1,s2≥0
在上一篇文章中我是以分块矩阵的形式来表现基变量和非基变量,这里我们有更加直观和简单的方法:把约束方程组中的基变量统一移到等式左侧,非基变量统一移到等式右侧,而目标则通过消元操作用非基变量表示:
m a x Z = 40 x 1 + 50 x 2 s . t . s 1 = 40 − x 1 − 2 x 2 s 2 = 120 − 4 x 1 − 3 x 2 x 1 , x 2 , s 1 , s 2 ≥ 0 \begin{aligned} max\quad& Z=40x_1+50x_2\\ s.t.\quad& s_1=40-x_1-2x_2 \\ & s_2=120-4x_1-3x_2 \\ & x_1,x_2,s_1,s_2\geq0 \end{aligned} maxs.t.Z=40x1+50x2s1=40−x1−2x2s2=120−4x1−3x2x1,x2,s1,s2≥0
然后令非基变量 x 1 = x 2 = 0 x_1=x_2=0 x1=x2=0,则各个基变量则等于各个约束中的常数项,即 s 1 = 40 , s 2 = 120 s_1=40,s_2=120 s1=40,s2=120,目标值 Z = 0 Z=0 Z=0,我们得到一个初始的可行解
x = [ x 1 x 2 s 1 s 2 ] = [ 0 0 40 120 ] \boldsymbol{x}= \begin{bmatrix} x_1\\ x_2\\ s_1\\ s_2 \end{bmatrix}= \begin{bmatrix} 0\\ 0\\ 40\\ 120 \end{bmatrix} x=⎣⎢⎢⎡x1x2s1s2⎦⎥⎥⎤=⎣⎢⎢⎡0040120⎦⎥⎥⎤
有了初始可行解,就可以开始整个算法的迭代过程,每次迭代需要有基变量的出基和非基变量的入基。
首先需要选择一个要变成基变量的非基变量,这里我们必须要选择在约束中系数为负数的变量,在这个例子中,我们可以选择变量 x 1 x_1 x1,它在两个约束等式中的系数都是负数:
为什么一定要系数为负数? 因为如果是正数,把它移到等式左侧进行规整时等式两边就需要除以一个负数,那么最终约束等式中将存在负数常数项,无法得到可行解。
我们选择了 x 1 x_1 x1作为入基的变量,可以看到两个约束等式都可以让 x 1 x_1 x1移到等式左侧,也就意味着基变量 s 1 s_1 s1和 s 2 s_2 s2都是可选的出基变量,我们先随机选择一个来试下效果。先选择让 s 1 s_1 s1出基,即对第一个等式进行互换操作:
s 1 = 40 − x 1 − 2 x 2 ⟹ x 1 = 40 − s 1 − 2 x 2 s_1=40-x_1-2x_2 \\ \Longrightarrow x_1=40-s_1-2x_2 s1=40−x1−2x2⟹x1=40−s1−2x2
然后将 x 1 x_1 x1带入第二个等式来消去里面的 x 1 x_1 x1:
s 2 = 120 − 4 x 1 − 3 x 2 ⟹ s 2 = 120 − 4 ( 40 − s 1 − 2 x 2 ) − 3 x 2 ⟹ s 2 = − 40 + 4 s 1 − 3 x 2 s_2=120-4x_1-3x_2 \\ \Longrightarrow s_2=120-4(40-s_1-2x_2)-3x_2 \\ \Longrightarrow s_2=-40+4s_1-3x_2 s2=120−4x1−3x2⟹s2=120−4(40−s1−2x2)−3x2⟹s2=−40+4s1−3x2
这样新的基变量表示为:
x 1 = 40 − s 1 − 2 x 2 s 2 = − 40 + 4 s 1 − 3 x 2 \begin{aligned} & x_1=40-s_1-2x_2 \\ & s_2=-40+4s_1-3x_2 \end{aligned} x1=40−s1−2x2s2=−40+4s1−3x2
但是可以看到变量 s 2 s_2 s2的等式中常数项为负数,也就是说对应的基解也不可行,说明我们选择 s 1 s_1 s1出基是不可行的;因此选择出基变量不可以随机选择,在这里我们需要采取这样的策略:
通过这个规则可以确保转移操作后解的可行性。回到我们的例子,在选择 x 1 x_1 x1为入基变量后,分别计算各个 b i − a i e \frac{b_i}{-a_{ie}} −aiebi值,可以看到第二个等式的这个值是最小的,所以应该用变量 s 2 s_2 s2来出基
经过转换,可以得到:
m a x Z = 1200 − 10 s 2 + 20 x 2 x 1 = 30 − 1 4 s 2 − 3 4 x 2 s 1 = 10 + 1 4 s 2 − 5 4 x 2 \begin{aligned} max\quad& Z=1200-10s_2+20x_2\\ & x_1=30-\frac{1}{4}s_2-\frac{3}{4}x_2 \\ & s_1=10+\frac{1}{4}s_2-\frac{5}{4}x_2 \end{aligned} maxZ=1200−10s2+20x2x1=30−41s2−43x2s1=10+41s2−45x2
对应了一个可行解 x 1 = 30 , x 2 = 0 , s 1 = 10 , s 2 = 0 x_1=30,x_2=0,s_1=10,s_2=0 x1=30,x2=0,s1=10,s2=0,目标值为1200
一般我们把这个变量 x e x_e xe和变量 x l x_l xl交换操作称之为 p i v o t ( e , l ) pivot(e,l) pivot(e,l)
我们需要判断当前可行解的目标值是否是最优来停止算法的迭代,这里有这样的判断标准:
继续上面的例子,在进行 p i v o t ( x 1 , s 2 ) pivot(x_1,s_2) pivot(x1,s2)后,目标函数的形式为:
Z = 1200 − 10 s 2 + 20 x 2 Z=1200-10s_2+20x_2 Z=1200−10s2+20x2
可以看到 x 2 x_2 x2的系数为正,这也就意味着如果我们在不违反约束的情况下继续增大 x 2 x_2 x2,目标值仍会更大,所以这时的基可行解 ( x 1 = 30 , x 2 = 0 , s 1 = 10 , s 2 = 0 ) (x_1=30,x_2=0,s_1=10,s_2=0) (x1=30,x2=0,s1=10,s2=0)不是最优的,所以可以继续选择变量进行 p i v o t pivot pivot迭代,并且下一次迭代的非基变量就从目标函数中系数大于零的变量中选择
因为现在只有变量 x 2 x_2 x2的系数为大于零,所以下一次迭代选择变量 x 2 x_2 x2入基;同样应用上面叙述的出基规则,选择变量 s 1 s_1 s1为出基变量:
m a x Z = 1200 − 10 s 2 + 20 x 2 x 1 = 30 − 1 4 s 2 − 3 4 x 2 s 1 = 10 + 1 4 s 2 − 5 4 x 2 \begin{aligned} max\quad& Z=1200-10s_2+20x_2\\ & x_1=30-\frac{1}{4}s_2-\frac{3}{4}x_2 \\ & s_1=10+\frac{1}{4}s_2-\frac{5}{4}x_2 \end{aligned} maxZ=1200−10s2+20x2x1=30−41s2−43x2s1=10+41s2−45x2
⟹ m a x Z = 1360 − 16 s 1 − 6 s 2 x 1 = 24 − 2 5 s 2 + 3 5 s 1 x 2 = 8 + 1 5 s 2 − 4 5 s 1 \Longrightarrow \begin{aligned} max\quad& Z=1360-16s_1-6s_2\\ & x_1=24-\frac{2}{5}s_2+\frac{3}{5}s_1 \\ & x_2=8+\frac{1}{5}s_2-\frac{4}{5}s_1 \end{aligned} ⟹maxZ=1360−16s1−6s2x1=24−52s2+53s1x2=8+51s2−54s1
这时的基可行解为 ( x 1 = 24 , x 2 = 8 , s 1 = 0 , s 2 = 0 ) (x_1=24,x_2=8,s_1=0,s_2=0) (x1=24,x2=8,s1=0,s2=0),目标值为1360;同时可以看到目标函数 Z = 1360 − 16 s 1 − 6 s 2 Z=1360-16s_1-6s_2 Z=1360−16s1−6s2中变量所有的系数都小于零,这也就意味着1360就是最优目标值,我们已经找到了问题的最优解。
现在可以总结一些单纯形算法的核心过程,如下面的伪代码所示:
w h i l e ∃ 1 ≤ i ≤ n : c i ≥ 0 d o c h o o s e e s u c h t h a t c e ≥ 0 l = arg min i : a i e < 0 b i − a i e p i v o t ( e , l ) \begin{aligned} while\quad&\exist1\leq i\leq n :c_i \geq 0 \quad do\\ &choose\ e\ such\ that\ c_e\geq0\\ &l= \underset {i:a_{ie}<0} {\operatorname {arg\,min} } \frac{b_i}{-a_{ie}}\\ &pivot(e,l) \end{aligned} while∃1≤i≤n:ci≥0dochoose e such that ce≥0l=i:aie<0argmin−aiebipivot(e,l)
现在考虑一下一些异常情况。首先看这个例子,假设下面这个优化模型是按照上面的算法过程进行到某一步时的情况:
m a x Z = 6 + 3 x 1 + 3 x 2 x 3 = 1 + 3 x 1 − 2 x 2 x 4 = 2 + 2 x 1 + x 2 x 5 = 3 + x 1 − 3 x 2 x 1 , x 2 , x 3 , x 4 , x 5 ≥ 0 \begin{aligned} max\quad& Z=6+3x_1+3x_2\\ & x_3=1+3x_1-2x_2 \\ & x_4=2+2x_1+x_2\\ & x_5=3+x_1-3x_2\\ & x_1,x_2,x_3,x_4,x_5\geq 0 \end{aligned} maxZ=6+3x1+3x2x3=1+3x1−2x2x4=2+2x1+x2x5=3+x1−3x2x1,x2,x3,x4,x5≥0
因为目标函数中 x 1 x_1 x1和 x 2 x_2 x2的系数都是正数,都可以选择为入基变量;如果选择 x 1 x_1 x1,再看约束等式中的情况,此时会发现约束等式中所有 x 1 x_1 x1的系数都是正数,这就意味着如果我们不断加大 x 1 x_1 x1的值,目标值也不断加大,同时所有约束都不违反,说明这个问题是无界的,找不到最优解;如果出现无界,那么肯定是最初的问题模型建立存在某些错误,相应的,算法的迭代中也需要加入无界的检查操作
再看下面的例子:
m a x Z = 5 + 3 x 1 + 3 x 2 x 3 = 0 − 3 x 1 − 2 x 2 x 4 = 2 − 2 x 1 + x 2 x 5 = 3 + x 1 − 3 x 2 x 1 , x 2 , x 3 , x 4 , x 5 ≥ 0 \begin{aligned} max\quad& Z=5+3x_1+3x_2\\ & x_3=0-3x_1-2x_2 \\ & x_4=2-2x_1+x_2\\ & x_5=3+x_1-3x_2\\ & x_1,x_2,x_3,x_4,x_5\geq 0 \end{aligned} maxZ=5+3x1+3x2x3=0−3x1−2x2x4=2−2x1+x2x5=3+x1−3x2x1,x2,x3,x4,x5≥0
第一个约束等式的常数项为零,这时我们如果继续进行pivot操作,常数项为零的等式的基变量会被选择为出基变量,代入到目标函数消元后,会发现目标函数的常数项仍然为5,而选择的那个约束等式转换后的常数项仍为零;继续进行pivot,会陷入这个零的情况,导致死循环,算法无法退出。
我们称这种情况为退化,为了处理这种情况,最常用的一种手段是再进行pivot选择时严格遵循Bland’s Rule:
Bland’s Rule进一步细化了算法中pivot选择变量的规则,去除所有的随机选择可能,需要注意的是这个规则是为了避免出现退化,而不是从退化情况跳出。
到这里算法的主体部分已经都明确了,但其实关于算法的初始解我们还没有详细讨论,在上面的例子里我们是默认让初始的松弛形式的松弛变量作为基变量,得到了一个可行解作为初始解,这在大多数情况下是可以的,但是当原始问题约束不等式中存在某个 b i < 0 b_i<0 bi<0时,这样的处理方法将不可行。例如这个线性规划问题:
m a x Z = x 1 + 2 x 2 x 1 + x 2 ≤ 2 − x 1 − x 2 ≤ − 1 x 1 , x 2 ≥ 0 \begin{aligned} max\quad& Z=x_1+2x_2\\ & x_1+x_2\leq 2\\ & -x_1-x_2\leq -1\\ & x_1,x_2\geq 0 \end{aligned} maxZ=x1+2x2x1+x2≤2−x1−x2≤−1x1,x2≥0
它的松弛形式:
m a x Z = x 1 + 2 x 2 x 1 + x 2 + s 1 = 2 − x 1 − x 2 + s 2 = − 1 x 1 , x 2 , s 1 , s 2 ≥ 0 ⟹ m a x Z = x 1 + 2 x 2 s 1 = 2 − x 1 − x 2 s 2 = − 1 + x 1 + x 2 x 1 , x 2 , s 1 , s 2 ≥ 0 \begin{aligned} max\quad& Z=x_1+2x_2\\ & x_1+x_2+s_1=2\\ & -x_1-x_2+s_2=-1\\ & x_1,x_2,s_1,s_2\geq 0 \end{aligned}\\ \Longrightarrow \begin{aligned} \\ max\quad& Z=x_1+2x_2\\ & s_1=2-x_1-x_2\\ & s_2=-1+x_1+x_2\\ & x_1,x_2,s_1,s_2\geq 0 \end{aligned} maxZ=x1+2x2x1+x2+s1=2−x1−x2+s2=−1x1,x2,s1,s2≥0⟹maxZ=x1+2x2s1=2−x1−x2s2=−1+x1+x2x1,x2,s1,s2≥0
这时得到的基解中, s 2 = − 1 s_2=-1 s2=−1,这显然不可行。因此在完整的单纯形算法中,解的初始化问题也是非常重要的一步,有两个问题需要在初始化中解决:
关于初始化的详细讨论,我把它放到后面一篇文章中,因为初始化还需要用到另一个非常巧妙的算法。