单纯形法(Simplex Method)是线性规划(Linear Programming)中最经典、应用最广泛的求解算法,由 George Dantzig 在1947年提出。它通过迭代方式在可行域的顶点(极值点)之间移动,逐步逼近最优解。
目录
- 算法背景与核心思想
- 线性规划标准型与基本概念
- 单纯形法的数学推导
- 单纯形表与算法步骤
- 手工计算实例(生产计划优化)
- 退化问题与应对策略
- Python代码实现
- 算法总结与局限性
一、算法背景与核心思想
1. 历史背景
- 提出者:George Dantzig(1947年)
- 应用领域:资源分配、生产计划、物流调度等线性规划问题。
- 核心目标:在满足线性约束条件下,找到使目标函数最大化的解。
2. 几何与代数视角
- 几何解释:可行域是一个凸多面体,最优解必出现在顶点(极值点)上。
- 代数操作:通过**基变量(Basic Variables)**的转换,在顶点之间移动,逐步逼近最优解。
二、线性规划标准型与基本概念
1. 标准型要求
最大化 c T x 约束条件 A x = b x ≥ 0 \begin{align*} \text{最大化} \quad & \mathbf{c}^T \mathbf{x} \\ \text{约束条件} \quad & A\mathbf{x} = \mathbf{b} \\ & \mathbf{x} \geq 0 \end{align*} 最大化约束条件cTxAx=bx≥0
- 目标函数需为最大化,变量非负。
- 所有约束为等式(通过添加松弛变量或剩余变量实现)。
2. 关键概念
术语 | 定义 |
---|---|
基变量 | 约束矩阵 ( A ) 中线性无关列对应的变量(数量等于约束方程个数) |
非基变量 | 不在基中的变量(值为0) |
可行基解 | 满足所有约束的基解 |
检验数(σ) | 目标函数对非基变量的灵敏度(判断是否达到最优) |
三、单纯形法的数学推导
1. 基变换与目标函数
- 设基变量为
x
B
\mathbf{x}_B
xB,非基变量为
x
N
\mathbf{x}_N
xN,约束方程可写为:
x B = B − 1 b − B − 1 N x N \mathbf{x}_B = B^{-1}\mathbf{b} - B^{-1}N\mathbf{x}_N xB=B−1b−B−1NxN - 目标函数转换为:
z = c B T B − 1 b + ( c N T − c B T B − 1 N ) x N z = \mathbf{c}_B^T B^{-1}\mathbf{b} + \left( \mathbf{c}_N^T - \mathbf{c}_B^T B^{-1}N \right) \mathbf{x}_N z=cBTB−1b+(cNT−cBTB−1N)xN - 检验数: σ j = c j − c B T B − 1 A j \sigma_j = c_j - \mathbf{c}_B^T B^{-1} A_j σj=cj−cBTB−1Aj,当所有 σ j ≤ 0 \sigma_j \leq 0 σj≤0 时达到最优。
2. 入基与出基规则
- 入基变量:选择 σ j > 0 \sigma_j > 0 σj>0 中最大的非基变量 x j x_j xj(最速上升)。
- 出基变量:通过最小比值测试 θ i = b i / A i j \theta_i = b_i / A_{ij} θi=bi/Aij 确定。
四、单纯形表与算法步骤
1. 单纯形表结构
基变量 | x 1 x_1 x1 | x 2 x_2 x2 | … | s 1 s_1 s1 | s 2 s_2 s2 | 右端项 |
---|---|---|---|---|---|---|
s 1 s_1 s1 | 2 | 1 | … | 1 | 0 | 8 |
s 2 s_2 s2 | 1 | 3 | … | 0 | 1 | 9 |
z z z | -3 | -4 | … | 0 | 0 | 0 |
2. 算法步骤
- 初始化:
- 将问题转化为标准型,确定初始基变量和非基变量。
- 构建初始单纯形表(包含目标函数和约束条件)。
- 最优性检验:
- 计算检验数(Reduced Costs) σ j = c j − c B T B − 1 A j \sigma_j = c_j - \mathbf{c}_B^T B^{-1} A_j σj=cj−cBTB−1Aj。
- 若所有 σ j ≤ 0 \sigma_j \leq 0 σj≤0,当前解为最优解,算法终止。
- 选择入基变量:
- 选取检验数 σ j > 0 \sigma_j > 0 σj>0 中最大的变量 x j x_j xj 入基(最速上升规则)。
- 选择出基变量:
- 计算右端项 b \mathbf{b} b 与入基变量列的正向比值 θ i = b i / A i j \theta_i = b_i / A_{ij} θi=bi/Aij(仅限 A i j > 0 A_{ij} > 0 Aij>0)。
- 选择最小 θ i \theta_i θi 对应的基变量 x i x_i xi 出基(保证可行性)。
- 更新基变量与单纯形表:
- 通过高斯消元(枢轴运算)更新基矩阵和目标函数。
- 重复步骤2-5直至满足最优性条件。
五、手工计算实例(生产计划优化)
1. 问题描述
- 最大化利润: z = 3 x 1 + 4 x 2 z = 3x_1 + 4x_2 z=3x1+4x2
- 约束条件:
{ 2 x 1 + x 2 ≤ 8 x 1 + 3 x 2 ≤ 9 x 1 , x 2 ≥ 0 \begin{cases} 2x_1 + x_2 \leq 8 \\ x_1 + 3x_2 \leq 9 \\ x_1, x_2 \geq 0 \end{cases} ⎩ ⎨ ⎧2x1+x2≤8x1+3x2≤9x1,x2≥0
2. 标准化与初始表
添加松弛变量
s
1
,
s
2
s_1, s_2
s1,s2,得到:
2
x
1
+
x
2
+
s
1
=
8
x
1
+
3
x
2
+
s
2
=
9
z
−
3
x
1
−
4
x
2
=
0
\begin{align*} 2x_1 + x_2 + s_1 &= 8 \\ x_1 + 3x_2 + s_2 &= 9 \\ z - 3x_1 - 4x_2 &= 0 \end{align*}
2x1+x2+s1x1+3x2+s2z−3x1−4x2=8=9=0
初始单纯形表:
基变量 | x 1 x_1 x1 | x 2 x_2 x2 | s 1 s_1 s1 | s 2 s_2 s2 | 右端项 |
---|---|---|---|---|---|
s 1 s_1 s1 | 2 | 1 | 1 | 0 | 8 |
s 2 s_2 s2 | 1 | 3 | 0 | 1 | 9 |
z z z | -3 | -4 | 0 | 0 | 0 |
3. 第一次迭代
- 入基变量: x 2 x_2 x2(检验数-4绝对值最大,但需注意符号,此处应为正值)
- 出基变量:计算 θ 1 = 8 / 1 = 8 \theta_1 = 8/1 = 8 θ1=8/1=8, θ 2 = 9 / 3 = 3 \theta_2 = 9/3 = 3 θ2=9/3=3,选 s 2 s_2 s2 出基。
- 枢轴运算:以 x 2 x_2 x2 列和 s 2 s_2 s2 行的3为主元,更新表格:
更新后表格:
基变量 | x 1 x_1 x1 | x 2 x_2 x2 | s 1 s_1 s1 | s 2 s_2 s2 | 右端项 |
---|---|---|---|---|---|
s 1 s_1 s1 | 5/3 | 0 | 1 | -1/3 | 5 |
x 2 x_2 x2 | 1/3 | 1 | 0 | 1/3 | 3 |
z z z | -1/3 | 0 | 0 | 4/3 | 12 |
4. 第二次迭代
- 入基变量: x 1 x_1 x1(检验数-1/3对应实际为正值)
- 出基变量:计算 θ 1 = 5 / ( 5 / 3 ) = 3 \theta_1 = 5/(5/3)=3 θ1=5/(5/3)=3,选 s 1 s_1 s1 出基。
- 枢轴运算:以 x 1 x_1 x1 列和 s 1 s_1 s1 行的5/3为主元,更新表格:
最终表格:
基变量 | x 1 x_1 x1 | x 2 x_2 x2 | s 1 s_1 s1 | s 2 s_2 s2 | 右端项 |
---|---|---|---|---|---|
x 1 x_1 x1 | 1 | 0 | 3/5 | -1/5 | 3 |
x 2 x_2 x2 | 0 | 1 | -1/5 | 2/5 | 2 |
z z z | 0 | 0 | 1/5 | 11/5 | 13 |
最优解: x 1 = 3 , x 2 = 2 x_1 = 3, x_2 = 2 x1=3,x2=2,最大利润 z = 3 × 3 + 4 × 2 = 17 z = 3 \times 3 + 4 \times 2 = 17 z=3×3+4×2=17。
六、退化与应对策略
1. 退化现象
- 基变量取值为0时,迭代后目标函数值不改变。
- 后果:可能进入循环,无法收敛。
2. 解决方案
- Bland规则:优先选择下标最小的变量入基和出基。
- 扰动法:对右端项 b \mathbf{b} b 施加微小随机扰动。
七、Python代码实现
使用 scipy.optimize.linprog
求解上述生产计划问题:
from scipy.optimize import linprog
# 目标函数系数(linprog默认最小化,需取负)
c = [-3, -4]
# 不等式约束矩阵
A = [[2, 1], [1, 3]]
b = [8, 9]
# 变量非负约束
x_bounds = (0, None)
# 求解
result = linprog(c, A_ub=A, b_ub=b, bounds=[x_bounds, x_bounds], method='highs')
print("最优解:x1 =", round(result.x[0], 2), "x2 =", round(result.x[1], 2))
print("最大利润:", -round(result.fun, 2))
输出:
最优解:x1 = 3.0 x2 = 2.0
最大利润:17.0
八、算法总结与局限性
优势
- 理论成熟,实际应用广泛。
- 对中小规模问题效率极高。
- 支持灵敏度分析(如影子价格、松弛变量分析)。
局限性
- 最坏情况下时间复杂度为指数级(如 Klee-Minty 问题)。
- 无法直接处理整数约束(需结合分支定界法)。
- 对大规模稀疏问题效率低于内点法。