运筹学 Part 1——线性规划问题求解方法#
这个学期我选了大三的运筹学,所谓的运筹学,就是在约束条件下如何求解一个函数的极值。像CS229中的SVM部分(KKT条件推导),而我们的最为简单的约束条件以及目标函数就是线性函数 ,因此线性规划问题的求解就非常的重要
线性规划问题的一般形式#
目标函数 :由于目标函数为线性的,我们要求 cTx 的最值,由于 max cTx⇔min −cTx ,从而对于任意线性规划问题,**目标函数可以转换成 min cTx
线性约束 :对于我们的约束而言,可能有 Ax≥b,Ax=b,Ax≤b ,对于等式是最好处理的,我们考虑将不等式化为等式=>考虑高中数竞中的小技巧:将差值设出来, Ax≥b⇔Ax−s=b,s≥0 ,其中s的维度和A的行数相同。
同样的道理有 Ax≤b⇔Ax+t=b,t≥0 从而我们所有的约束条件都可以表示成 Ax=b 的形式 (最多添加几个人工变量)
变量范围: 在线性约束中我们添加了若干个人工变量,这些变量的都是大于等于0的,因此我们希望对于原有变量也全部大于等于0.
- 对于某一个变量 xi 而言,如果 xi 有下界,那么用 xi′(xi−lowbound) 代替原有的 xi 即有 xi′≥0
- 若 xi 没有下界,使用 xi=yi−zi,yi≥0,zi≥0 代替从而有所有变量都有下界0
- 如果 xi 有上界 xi≤ui ,添加人工变量 yi 以及约束 xi+yi=ui 此时有 ui≥0 ,对于 xi 的处理方法同没有下界
经过以上的操作,我们有 \begin{align*} \min \quad & \mathbf{c}^\top \mathbf{x} \\ \text{s.t.} \quad & A\mathbf{x} = \mathbf{b} \\ & \mathbf{x} \geq \mathbf{0}. \end{align*} 这样的形式称为线性规划的一般形式
线性规划解的不同刻画(简略)#
我们来介绍几个概念
- 可行域 P={x∈Rn∣Ax=b,x≥0} 称为问题的可行域
- 凸集 :一个集合P称为凸集, ∀A,B∈P,λ∈[0,1]⇒λA+(1−λB)∈P
容易发现所有线性规划问题的可行域都是凸集
对于线性规划问题的解,我们在高中的时候做题,顶点,边界为常见的极值点的位置,我们想要刻画这个顶点概念
- 极点 : ∃C,∀x∈P⇒cTx>cTx^ 此时我们称 x^ 为极点(一个多面体的一个角)
- 顶点 :x称为P的顶点,如果 ∀A,B∈P(A,B=x),λ∈[0,1],x=λA+(1−λ)B (也就是不存在有点与它共线)
可以发现顶点和极点是”等价的”,但是在计算的时候并不方便,于是有
- 基解 :如果x为n维向量,在P中的n个线性无关的约束构成的线性方程组有唯一解,这个解为P的一个基解
- 可行基解 :如果基解在P中,那么称之为P的可行基解

可以证明:在线性规划问题(凸集)中,极点,顶点,可行基解是等价的,对于线性规划的极值,要么在无穷处取到,要么可以在一个极点处取到
从而我们可以将一个不容易计算的** “顶点,角”**转换称计算一个线性方程组的问题。
对于这个定理的证明可以看我的手写笔记:

单纯形法#
如果我们直接将每一个可行基解求出来,然后计算其对应的目标值,总共的基解数目为 Cmn ,而对于求解一个线性方程组,也就是求逆的操作,复杂度为 n3 ,总共就是 Cmnn3 ,太大了。
因此单纯形法的思路就是** 从一个基解开始,通过一个损失函数找到可以使目标函数进行改善的下一个可行基解,然后进行解的变换并继续迭代下去**
损失函数#
对于标准的线性规划问题,我们一个可行基解什么时候是最优的,此时我们借鉴** 理论力学中的虚功原理:找一个小的位移!!**
由于我们的可行基解可以看成若干个平面的交对应的顶点,如果想要移动到一个新的基解上,我们考虑的是进行** 顶点对应平面的替换**
即为如果 xB 是由 A1,A2,...An 构成的,那么就对于平面进行替换成 A1′,A2′,....An′ 得到 xB′
而对于平面的替换的话,参考排序算法,自然想到进行一一的替换 (A1,A2,...An)⇒(A1′,A2,...An)⇒(A1′,A2′,...An′)
从而可以做到依次的抛弃 A1,...An 这些平面
如果对于 Ax=b,x~=x+θd 进行可行基的划分,就是 A=(B,N),x=(xB,xN)T,c=(cB,cN)T,d=(dB,dN)T
而依次替换一个平面就是在数学上的表示就是
{dj=1,di=0,j∈N,i∈N,i=j,
由于移动后的点需要满足整体约束(在这个多边形表面上),有 0=Ad=BdB+Aj⇒dB=−B−1Aj

也就是图中的 A2,A3 交线的方向向量
考虑进行一些小扰动,目标函数就有
z=cB⊤(xB+θdB)+θcN⊤dN=cB⊤(xB−θB−1Aj)+θcj
相比于原目标 cTx 变化量为 z−c⊤x=θ(cj−cB⊤B−1Aj)
因此对于** 远离j这个基方向对于目标函数的影响,就是 c^j=cj−cB⊤B−1Aj** ,从而损失函数也可以理解成对于这个** 基方向的导数!**
转轴运算#
如果我们最优判定中,对于某一个可行基解 x=(B−1b 0) 可以计算其损失函数,如果对于某一个基方向 xj 有损失函数(导数) cj<0 ,就说明对于j** 这个基方向进行移动,目标函数会减小**,这是我们需要的
具体操作为
对于 x~=x+θd,d=−B−1Aj ,即为
x~B1x~B2⋮x~Bm=xB1xB2⋮xBm+θdB1dB2⋮dBm.
- dBi≥0 恒成立,如果有 cj<0 ,那么在该点不是最值,沿着 dB 目标函数会不断增加,因此此时为无界解
- 如果存在i,有 dBi<0 ,那么在该方向上继续移动,会有移动距离的上限 , θ≤−xBi/dBi ,因此我们取最大的 θ ,记成 \begin{equation} \theta^* = \min_{\{i \mid d_{B_i} < 0\}} \left( -\frac{x_{B_i}}{d_{B_i}} \right). \end{equation}
从而我们得到了新的可行解
x~=(xB1,⋯,xBr−1,0,xBr+1,⋯,xBm,0,⋯,xj,⋯,0)⊤
从而通过替换基变量 xBr 与非基变量 xj 得到新的可行解,这就是转轴运算
** 对于单纯形法的几何&代数的对应:**
- 可行基解 就是多面体的顶点
- 而损失函数(最优判定条件) 就是目标函数对于变量 xj 的导数
- 每一步我们都找到一个 ∂xj∂cTx(cj−cB⊤B−1Aj) 小于0的方向 (对应第一部分)
- 然后沿着这个方向进行移动,直到找到第一个可行基解(转轴运算 )
记住这个图片之后,再翻译成代数语言就可以了
单纯形表法#
由于我们的单纯形法每一步将可行基解中的一个变量从大于0变成0,并且将另外一个0变成大于0
其对应的基矩阵 B⇒B~ 每一次进行迭代之后会添加一列,再减少一列 ,可以发现 B−1,B~−1 之间会有许多关联
考虑相乘,可以发现 B−1B~=[e1,e2,…,er−1,ur,er+1,…,em] 中, ur=−dB=B−1Aj
因此只需要对于 B−1A 进行线性变换,将 ur 对应的列变成 er ,就可以得到 B~−1A
更进一步,我们可以同时对于入基变量,损失函数(导数),函数值进行同样的线性变换,组成一张表,每一步的操作就是对于表的线性变换
[−cB⊤B−1bB−1bc⊤−cB⊤B−1AB−1A]
- Step1 找到导数小于0的index:在第一行中找到一个值小于0的值对应的列prepare index
- Step2 找到在该方向上的第一个”平面”:在prepare index这一列上找到比值 \begin{equation} \theta^* = \min_{\{i \mid d_{B_i} < 0\}} \left( -\frac{x_{B_i}}{d_{B_i}} \right). \end{equation}
并且进行入基,出基操作
一个例子为

初始可行基解的寻找#
对于初始的可行基解的寻找,我们希望可以有类似与例子一样的基矩阵为单位阵的结构,观察到例子中的线性规划方程为 Ax≤b 的变种,即为 Ax+y=b ,因此我们也进行同样的构造。考虑辅助问题
\begin{align*} \min \quad & \mathbf{1}^\top \mathbf{y} \\ \text{s.t.} \quad & A\mathbf{x} + \mathbf{y} = \mathbf{b} \\ & \mathbf{x} \geq \mathbf{0},\; \mathbf{y} \geq \mathbf{0}. \end{align*}
对于这个问题可行解非常容易发现为 (x,y)=(0,b) ,从而可以构造单纯形表,最后得到最优解
如果我们的原问题有可行解存在,那么可以取 (x,y)=(x,0) 此时有最小值为0,而由于 y≥0 因此目标函数 z≥0 ,因此假设我们辅助问题的最优解为 (x^,y^)
- 若 y^ 大于0,那么有 z=1Ty>0 ,说明原问题没有可行解
- 若 y^=0 此时得到的 x^ 就是一组可行解,从而可以进行正常的单纯形法
一些特殊的情况#
** 循环解情况:**
如果在单纯形表中入基变量中有多个0出现,那么就有可能发生两个变量 i,j 来回入,出,入,出的情况,因此我们在进行入基变量选取的时候** Bland rule可以保证最后一定收敛**(可能会牺牲一点点的复杂度)
- 若非基向量中,存在多个 ci^<0 选取i最小的下标
- 出基的过程中,计算下标r使得 \begin{equation} \theta^* = \min_{\{i \mid d_{B_i} < 0\}} \left( -\frac{x_{B_i}}{d_{B_i}} \right). \end{equation}
时,选取最小的下标 Br
退化解情况:
如果我们的可行解 x=(xB,xN),xN=0 理论上 xB 有A.rank的大小,但是如果 xB 中有0的话,那么就无法分辨那个是入基变量,那个是别的了,对此我们可以:
** 直接从所有的 xi=0 中抽取若干个补充到 xB 中,这样找到的基矩阵也是满足要求的**
这样做有个前提是矩阵A是满秩的,这样对于任意抽取 A.rank 列也是线性无关的,可以作为基矩阵。
代码实现#
关于实验要求以及我的代码可以参考我的github仓库
以上我们介绍了线性规划问题的求解思路,从问题的化简,到转换成计算机可以理解的语言,最后到单纯形法这种为了节省时间复杂度设计的东西,一节可能的特殊情况,同时我们也从几何角度理解了单纯形法的变换过程,最后我们进行了代码实现部分。总体而言,单纯形法还是非常漂亮美丽的算法