这里写目录标题
- 线性规划例子
- 啤酒厂问题
- 图解法
- 单纯形法数学推导
- 将问题标准化并转为矩阵形式
- 开始推导
- 实例
- 图解法
- 单纯形法
线性规划例子
啤酒厂问题
- 每日销售上限:100箱啤酒
- 营业时间:14小时
- 生产1箱生啤需1小时
- 生产1箱黑啤需2小时
- 生啤售价:20美元/箱
- 黑啤售价:30美元/箱
目标:最大化利润
我们可以根据上述描述,建立数学模型。假设生啤生产 x 1 x_1 x1箱,黑啤生产 x 2 x_2 x2箱,那么就有如下的线性规划式子。
M a x x 1 , x 2 20 × x 1 + 30 × x 2 s . t . x 1 + 2 × x 2 < = 14 x 1 + x 2 < = 100 x 1 , x 2 > = 0 Max_{x_1,x_2} \qquad20 \times x_1 + 30 \times x_2 \\ \qquad s.t. \qquad \qquad x_1 + 2\times x_2<=14 \\\qquad \qquad \qquad x_1+x_2<=100 \\\qquad \qquad \qquad x_1,x_2>=0 Maxx1,x220×x1+30×x2s.t.x1+2×x2<=14x1+x2<=100x1,x2>=0
图解法
通过求解上述的线性规划问题即可得到最优的生产计划。那么如何求解呢?根据图解法可以看出最优值为2400。而且可以观察到随着z的上下平移,最终取到的最优值必定是z与凸多边形(约束域)的某一个顶点相交得到。这样的话如果想求解一个线性规划问题,只需要遍历这个凸多边形(在高维空间中是凸多面体)的每一个顶点,对目标函数值大小进行比较即可。
单纯形法数学推导
将问题标准化并转为矩阵形式
那么如何将遍历各个顶点的过程转化为数学语言来表述呢,且是否真的需要将所有顶点遍历一遍,有没有更高效的遍历方法?
还是上述的问题,我们可以把问题先化为标准型再写成矩阵形式。
标准型:
M i n x 1 , x 2 − 20 × x 1 − 30 × x 2 s . t . x 1 + 2 × x 2 + x 3 = 14 x 1 + x 2 + x 4 = 100 x 1 , x 2 , x 3 , x 4 > = 0 Min_{x_1,x_2} \qquad-20 \times x_1 - 30 \times x_2 \\ \qquad s.t. \qquad \qquad x_1 + 2\times x_2 + x_3=14 \\\qquad \qquad \qquad x_1+x_2 + x_4 =100 \\\qquad \qquad \qquad x_1,x_2,x_3,x_4>=0 Minx1,x2−20×x1−30×x2s.t.x1+2×x2+x3=14x1+x2+x4=100x1,x2,x3,x4>=0
这个问题的矩阵形式为:
Minimize c ⊤ x Subject to: A x = b and x ≥ 0 \text{Minimize} \quad \mathbf{c}^\top \mathbf{x}\\ \text{Subject to:} \quad \mathbf{Ax} = \mathbf{b} \quad \text{and} \quad \mathbf{x} \geq \mathbf{0} Minimizec⊤xSubject to:Ax=bandx≥0
其中:
c = [ − 20 − 30 0 0 ] , x = [ x 1 x 2 x 3 x 4 ] , b = [ 14 100 ] \mathbf{c} = \begin{bmatrix} -20 \\ -30 \\ 0 \\ 0 \\ \end{bmatrix}, \quad \mathbf{x} = \begin{bmatrix} x_1 \\ x_2 \\ x_3 \\ x_4 \\ \end{bmatrix}, \quad \mathbf{b} = \begin{bmatrix} 14 \\ 100 \\ \end{bmatrix} c= −20−3000 ,x= x1x2x3x4 ,b=[14100]
约束矩阵 A \mathbf{A} A为:
A = [ 1 2 1 0 1 1 0 1 ] \mathbf{A} = \begin{bmatrix} 1 & 2 & 1 & 0 \\ 1 & 1 & 0 & 1 \\ \end{bmatrix} A=[11211001]
这里的变量 x 3 x_3 x3 和 x 4 x_4 x4 是松弛变量,用于将不等式约束转换为等式约束。
开始推导
令 x = [ x B , x N ] , A = [ B , N ] , c = [ c B , c N ] T x = [x_B,x_N],A = [B,N],c = [c_B,c_N]^T x=[xB,xN],A=[B,N],c=[cB,cN]T(注意这里的B一定要是一个可逆矩阵),初始化时令 x N x_N xN全为0.
那么原问题可以转化为如下形式
M i n i m i z e c B × x B + c N × x N s . t . B × x B + N × x N = b x B > = 0 , x N > = 0 Minimize \qquad c_B\times x_B + c_N\times x_N \\ s.t.\qquad \qquad \qquad B\times x_B + N\times x_N = b \\ \qquad \qquad \qquad x_B>=0,x_N>=0 MinimizecB×xB+cN×xNs.t.B×xB+N×xN=bxB>=0,xN>=0
将约束条件 B × x B + N × x N = b B\times x_B + N\times x_N = b B×xB+N×xN=b可以等价变形为 x B = B − 1 ( b − N × x N ) = B − 1 b x_B = B^{-1}(b - N\times x_N) = B^{-1}b xB=B−1(b−N×xN)=B−1b
注意:上面用到了 B − 1 B^{-1} B−1,这也是为什么说B得是可逆矩阵的原因,在后面的转化还用到了 x N x_N xN初始全为0。
将变换之后的式子带回目标函数 c B × x B + c N × x N c_B\times x_B + c_N\times x_N cB×xB+cN×xN就有 c B × B − 1 ( b − N × x N ) + c N × x N c_B\times B^{-1}(b - N\times x_N) + c_N\times x_N cB×B−1(b−N×xN)+cN×xN,进一步化简可以得到如下式子(由于在初始化时 x N x_N xN全为0)
c B B − 1 b + ( c N − c B B − 1 N ) x N = c B B − 1 b c_BB^{-1}b +(c_N - c_BB^{-1}N)x_N = c_BB^{-1}b cBB−1b+(cN−cBB−1N)xN=cBB−1b
可以观察到,目标函数还有进一步变小的可能,那就是 ( c N − c B B − 1 N ) x N (c_N - c_BB^{-1}N)x_N (cN−cBB−1N)xN之前由于 x N x_N xN全为0,所以这一项也为0,但如果 x N x_N xN中某一项的系数为负,将该项从0变为非0即可进一步减小目标函数。那么如何变更高效呢?就是找到负系数中,负系数绝对值最大的那一项(在x变化量一致的情况下,负系数绝对值越大,目标函数值下降的就越多)。
那么也可以推出,如果 ( c N − c B B − 1 N ) (c_N - c_BB^{-1}N) (cN−cBB−1N)全部为正,即使将 x N x_N xN的任意一项由0变为非0,目标函数值也不会降低,这时就可以说已经找到了目标函数的最小值,这也是算法的收敛准则: ( c N − c B B − 1 N ) > = 0 (c_N - c_BB^{-1}N)>=0 (cN−cBB−1N)>=0。
那么假如 ( c N − c B B − 1 N ) i (c_N - c_BB^{-1}N)_i (cN−cBB−1N)i是负系数中系数绝对值最大的的那一项,对应这一项的变量为 x N i x_{N_{i}} xNi,我们要将其由0变为非0,那么要变为多少呢?
注意到对于 x B x_B xB还有 x B > = 0 x_B>=0 xB>=0的约束。且有 x B = B − 1 ( b − N × x N ) x_B = B^{-1}(b - N\times x_N) xB=B−1(b−N×xN), x N x_N xN的变化也会引起 x B x_B xB的变化,我们应该使变化之后的 x B x_B xB仍然满足非负性。在 x B > = 0 x_B>=0 xB>=0的情况下, x N x_N xN要变得尽可能大。那么就有如下式子:
B − 1 ( b − N × x N ) > = 0 b − N × x N > = 0 B^{-1}(b - N\times x_N)>=0 \\b - N\times x_N>=0 \\ B−1(b−N×xN)>=0b−N×xN>=0
为了满足上述式子,而 x N i x_{N_{i}} xNi变得又要尽可能多,那么 x N i = m i n { b N } = b j N j x_{N_{i}} = min\{\frac{b}{N} \} = \frac{b_j}{N_j} xNi=min{Nb}=Njbj,这样的话同时就把 x B j x_{B_j} xBj变为0.
如此就完成了一轮迭代,不断迭代知道达到收敛准则为止。迭代的过程其实也是从一个端点替换到另一个端点的过程。
实例
对于线性规划问题
m a x 2 x 1 + 3 x 2 s . t . 4 x 1 + x 2 < = 16 − x 1 + x 2 < = 6 x 1 , x 2 > = 0 max \qquad 2x_1 + 3x_2 \\s.t. \qquad 4x_1 + x_2<=16 \\ \qquad -x_1+x_2<=6 \\ \qquad x_1,x_2>=0 max2x1+3x2s.t.4x1+x2<=16−x1+x2<=6x1,x2>=0
图解法
利用图解法可以看到红色的线是最优的等值线, x 1 = 2 , x 2 = 8 x_1 = 2,x_2 = 8 x1=2,x2=8.
单纯形法
将原来的线性规划化为标准型并写为矩阵形式
m i n − 2 x 1 − 3 x 2 s . t . 4 x 1 + x 2 + x 3 = 16 − x 1 + x 2 + x 4 = 6 x 1 , x 2 , x 3 , x 4 > = 0 min \qquad -2x_1 - 3x_2 \\s.t. \qquad 4x_1 + x_2+x_3=16 \\ \qquad -x_1+x_2+x_4=6 \\ \qquad x_1,x_2,x_3,x_4>=0 min−2x1−3x2s.t.4x1+x2+x3=16−x1+x2+x4=6x1,x2,x3,x4>=0
其中, x 3 x_3 x3 和 x 4 x_4 x4是松弛变量。
矩阵形式为:
目标函数: Maximize c ⊤ x \text{Maximize} \quad \mathbf{c}^\top \mathbf{x} Maximizec⊤x
其中, c = [ − 2 − 3 0 0 ] \mathbf{c} = \begin{bmatrix} -2 \\ -3 \\ 0 \\ 0 \end{bmatrix} c= −2−300 为目标函数的系数向量。
约束条件: A x = b \mathbf{Ax} = \mathbf{b} Ax=b,其中 A = [ 4 1 1 0 − 1 1 0 1 ] \mathbf{A} = \begin{bmatrix} 4 & 1 & 1 & 0 \\ -1 & 1 & 0 & 1 \end{bmatrix} A=[4−1111001]是约束系数矩阵, x = [ x 1 x 2 x 3 x 4 ] \mathbf{x} = \begin{bmatrix} x_1 \\ x_2 \\ x_3 \\ x_4 \end{bmatrix} x= x1x2x3x4 是决策变量向量, b = [ 16 6 ] \mathbf{b} = \begin{bmatrix} 16 \\ 6 \end{bmatrix} b=[166]是约束右端项向量。
因此,标准型的线性规划问题可以表示为:
Maximize c ⊤ x Subject to A x = b x ≥ 0 \begin{align*} \text{Maximize} \quad & \mathbf{c}^\top \mathbf{x} \\ \text{Subject to} \quad & \mathbf{Ax} = \mathbf{b} \\ & \mathbf{x} \geq 0 \end{align*} MaximizeSubject toc⊤xAx=bx≥0
那么根据上述单纯形法的推导, x B = [ x 3 , x 4 ] , x N = [ x 1 , x 2 ] , N = [ 4 1 − 1 1 ] , B = [ 1 0 0 1 ] , c B = [ 0 , 0 ] , c N = [ − 2 , − 3 ] x_B = [x_3,x_4],x_N = [x_1,x_2],N = \begin{bmatrix} 4 & 1 \\ -1 & 1 \end{bmatrix},B = \begin{bmatrix} 1 & 0 \\ 0 & 1 \end{bmatrix},c_B = [0,0],c_N = [-2,-3] xB=[x3,x4],xN=[x1,x2],N=[4−111],B=[1001],cB=[0,0],cN=[−2,−3]
初始时令 x N = [ 0 , 0 ] x_N = [0,0] xN=[0,0]
第一轮迭代:
此时 c N − c B B − 1 N = [ − 2 , − 3 ] c_N - c_BB^{-1}N = [-2,-3] cN−cBB−1N=[−2,−3]因此选择 x 2 x_2 x2作为入基变量更为高效,且 b − N x N = [ 16 − 4 x 1 − x 2 6 + x 1 − x 2 ] > = 0 b - Nx_N =\begin{bmatrix} 16 - 4x_1 - x_2 \\ 6 +x_1-x_2 \end{bmatrix}>=0 b−NxN=[16−4x1−x26+x1−x2]>=0故 x 2 = 6 x_2 = 6 x2=6并令 x 4 = 0 x_4 = 0 x4=0, x 2 x_2 x2入基, x 4 x_4 x4出基.
经过此轮迭代后,各变量如下
x B = [ x 3 , x 2 ] , x N = [ x 1 , x 4 ] , N = [ 4 0 − 1 1 ] , B = [ 1 1 0 1 ] , c B = [ 0 , − 3 ] , c N = [ − 2 , 0 ] x_B = [x_3,x_2],x_N = [x_1,x_4],N = \begin{bmatrix} 4 & 0 \\ -1 & 1 \end{bmatrix},B = \begin{bmatrix} 1 & 1 \\ 0 & 1 \end{bmatrix},c_B = [0,-3],c_N = [-2,0] xB=[x3,x2],xN=[x1,x4],N=[4−101],B=[1011],cB=[0,−3],cN=[−2,0]
第二轮迭代:
此时 c N − c B B − 1 N = [ − 5 , 3 ] c_N - c_BB^{-1}N = [-5,3] cN−cBB−1N=[−5,3],选择 x 1 x_1 x1作为入基变量更为高效,且 b − N x N = [ 16 − 4 x 1 6 + x 1 ] > = 0 b - Nx_N =\begin{bmatrix} 16 - 4x_1 \\ 6 +x_1 \end{bmatrix}>=0 b−NxN=[16−4x16+x1]>=0 故 x 1 = 4 x_1 = 4 x1=4并令 x 3 = 0 x_3 = 0 x3=0, x 1 x_1 x1入基, x 3 x_3 x3出基.
经过此轮迭代后,各变量如下
x B = [ x 1 , x 2 ] , x N = [ x 3 , x 4 ] , N = [ 1 0 0 1 ] , B = [ 4 1 − 1 1 ] , c B = [ − 2 , − 3 ] , c N = [ 0 , 0 ] x_B = [x_1,x_2],x_N = [x_3,x_4],N = \begin{bmatrix} 1 & 0 \\ 0 & 1 \end{bmatrix},B = \begin{bmatrix} 4 & 1 \\ -1 & 1 \end{bmatrix},c_B = [-2,-3],c_N = [0,0] xB=[x1,x2],xN=[x3,x4],N=[1001],B=[4−111],cB=[−2,−3],cN=[0,0]
第三轮迭代:
此时 c N − c B B − 1 N = [ 1 , 2 ] c_N - c_BB^{-1}N = [1,2] cN−cBB−1N=[1,2]都大于0,达到收敛条件,此时令 x N = [ 0 , 0 ] x_N = [0,0] xN=[0,0]解得 x B = [ 2 , 8 ] x_B = [2,8] xB=[2,8]最小值为28.
附:图解法绘图代码
import numpy as np
import matplotlib.pyplot as plt# 定义约束条件
x = np.linspace(0, 10, 400) # 创建x轴的值
eq1 = 16 - 4*x # 约束条件1: 4x1 + x2 <= 16 => x2 <= 16 - 4x1
eq2 = 6 + x # 约束条件2: -x1 + x2 <= 6 => x2 >= -x1 + 6# 画出约束条件
plt.plot(x, eq1, label=r'$4x_1 + x_2 \leq 16$')
plt.plot(x, eq2, label=r'$-x_1 + x_2 \leq 6$')
plt.xlim((0, 10))
plt.ylim((0, 20))
plt.xlabel(r'$x_1$')
plt.ylabel(r'$x_2$')# 标记可行域
plt.fill_between(x, np.minimum(eq1, eq2), where=(eq1 >= 0) & (eq2 >= 0), alpha=0.3)# 添加等值线
x_vals = np.linspace(0, 10, 100)
y_vals = np.linspace(0, 20, 100)
X, Y = np.meshgrid(x_vals, y_vals)
Z = 2*X + 3*Y
max_val = np.max(Z)# 调整等值线范围
plt.contour(X, Y, Z, levels=[28], colors='red')
plt.contour(X, Y, Z, levels=np.linspace(0, 50, 10), colors='grey', linestyles='dashed')# 添加标签和图例
plt.title('Feasible Region with Contour Lines')
plt.legend()
plt.grid(True)
plt.show()