对可行的线性规划问题,
{ minimize c ⊤ x s.t. A x = b x ≥ o ( 1 ) \begin{cases} \text{minimize}\quad\boldsymbol{c}^\top\boldsymbol{x}\\ \text{s.t.}\quad\quad\boldsymbol{Ax}=\boldsymbol{b}\\ \quad\quad\quad\quad\boldsymbol{x}\geq\boldsymbol{o} \end{cases}\quad\quad(1) ⎩
⎨
⎧minimizec⊤xs.t.Ax=bx≥o(1)
设 B \boldsymbol{B} B为一基矩阵, N \boldsymbol{N} N为对应的非基矩阵。在没遇到判别向量 z − c N ⊤ ≤ o \boldsymbol{z}-\boldsymbol{c}_{N}^\top\leq\boldsymbol{o} z−cN⊤≤o前,轴转操作可重复进行。每做一次,目标函数在新的基本可行解处的函数值都会有所减小。其中, z = c B ⊤ B − 1 N \boldsymbol{z}=\boldsymbol{c}_{\boldsymbol{B}}^\top\boldsymbol{B}^{-1}\boldsymbol{N} z=cB⊤B−1N。 z − c N ⊤ \boldsymbol{z}-\boldsymbol{c}_{N}^\top z−cN⊤称为判别向量。直至判别向量非正,则得到最优解。在轴转过程中,选的入基向量下标 e e e后,向量 y e = B − 1 α e \boldsymbol{y}_e=\boldsymbol{B}^{-1}\boldsymbol{\alpha}_e ye=B−1αe若非正,则遇到了无界问题,自然无需继续轴转。利用轴转操作时这两个向量的特性,我们可以对可行(具有初始基本可行解)的线性规划(1),写出如下的单纯形算法实现函数。
import numpy as np #导入numpy
def simplex(A, b, c, Bind, Nind):
B = A[:, Bind] #初始基矩阵
B1 = np.linalg.inv(B) #基矩阵的逆
w = np.matmul(c[Bind], B1)
z = np.matmul(w, A[:,Nind]).flatten() #构造判别式中的z
while not(z - c[Nind] <= 1e-10).all():
infinite, Bind, Nind, z=pivot(A, b, c, Bind, Nind, z) #轴转
if infinite:
print("无界问题...", end=',')
return None
return Bind
程序的第2~12行定义的函数simplex,参数A,b,c分别表示标准型线性规划(6.1)的等式约束系数矩阵 A \boldsymbol{A} A,常数向量 b \boldsymbol{b} b和目标函数系数向量 c \boldsymbol{c} c。Bind和Nind分别表示初始基矩阵 B \boldsymbol{B} B的列向量下标和非基矩阵 N \boldsymbol{N} N的列向量下标。函数体内第3行根据Bind计算基矩阵 B \boldsymbol{B} B赋予B。第4行计算 B − 1 \boldsymbol{B}^{-1} B−1赋予B1。第5~6行计算向量 z = c B ⊤ B − 1 N \boldsymbol{z}=\boldsymbol{c}_{\boldsymbol{B}}^\top\boldsymbol{B}^{-1}\boldsymbol{N} z=cB⊤B−1N。第7~11行的while循环,在 z − c N ⊤ \boldsymbol{z}-c_{\boldsymbol{N}}^\top z−cN⊤中存在正数元素的条件下迭代轴转操作。其中,第8行调用博文《最优化方法Python计算:标准型线性规划的轴转操作》定义的pivot函数,对当前的基矩阵 B \boldsymbol{B} B和非基矩阵 N \boldsymbol{N} N及向量 z \boldsymbol{z} z