线性规划 — 从零精通算法与数据结构——Google 面试系统备战 第17篇
第17章:线性规划
本章目标
读完本章你会:
- 将实际问题建模为标准型 / 松弛型线性规划
- 理解单纯形法的几何直觉(顶点跳跃)
- 阐述对偶性——原问题和对偶问题的最优值相等
- 将最大流、最短路径等图问题视为线性规划的特例
- 认识整数规划的 NP 难度
知识讲解
从一个生活例子开始
你经营一家面包店。一个面包利润 5 元,一个蛋糕利润 8 元。面粉有限:面包用 2 单位,蛋糕用 3 单位,总共 120 单位。鸡蛋有限:面包用 1 单位,蛋糕用 2 单位,总共 70 单位。每天最多工作 12 小时。
优化目标: 生产多少面包 (x₁) 和蛋糕 (x₂),使利润最大?
Maximize: 5x₁ + 8x₂
Subject to:
2x₁ + 3x₂ ≤ 120 (面粉约束)
x₁ + 2x₂ ≤ 70 (鸡蛋约束)
x₁, x₂ ≥ 0
这就是一个线性规划——目标函数和约束都是变量的线性函数。几何上,可行域是一个凸多边形,最优解出现在某个顶点。
工作原理
17.1 标准型与松弛型
标准型:
Maximize: cᵀx
Subject to: Ax ≤ b, x ≥ 0
松弛型: 引入松弛变量 s₁, s₂ 将不等式变为等式:
2x₁ + 3x₂ + s₁ = 120
x₁ + 2x₂ + s₂ = 70
x₁, x₂, s₁, s₂ ≥ 0
非基变量 = 0 ⇒ 基变量的值 ⇒ 获得一个顶点(基本可行解)。
17.2 单纯形法直觉
单纯形法从一个顶点开始,沿可行域的边走到相邻的更优顶点,直到不能再改进:
1. 找到一个初始可行解(顶点)
2. while 存在一个目标函数系数 > 0 的非基变量:
a. 选入基变量——哪个变量增加能最快提高目标值?
b. 选出基变量——哪个约束最先被"碰"到?
c. 枢轴操作——更新方程组,交换入基/出基变量的角色
3. 此时得到最优解
几何直觉: 在凸多面体的顶点间跳跃,每次只沿最陡的边前进。单纯形法在绝大多数情况下是多项式时间——虽然理论上存在反例(Klee-Minty cube)使得它指数时间。
17.3 对偶性
每个线性规划(原问题)都有对应的对偶问题:
原问题 (Primal):
Max cᵀx s.t. Ax ≤ b, x ≥ 0
对偶问题 (Dual):
Min bᵀy s.t. Aᵀy ≥ c, y ≥ 0
弱对偶定理: 原问题的任何可行解 ≤ 对偶问题的任何可行解。
强对偶定理: 若原问题有有限最优解 x,则对偶问题也有有限最优解 y,且 cᵀx* = bᵀy*。
直觉: 原问题在"最大化利润",对偶问题在"最小化资源的影子价格"。强对偶说:最优利润(原)= 最优定价(对偶)——市场出清。
17.4 图问题作为线性规划
最大流 = 线性规划:
Maximize: Σ f(s, v) (从源点出发的总流)
Subject to:
0 ≤ f(u, v) ≤ c(u, v) (容量约束)
Σ f(u, v) = Σ f(v, w) (流守恒——除 s,t 外)
最大流问题的特殊性:约束矩阵是全幺模的——单纯形法的所有顶点都是整数解。这就是为什么最大流总能找到整数流(如果容量是整数的话)。
最短路径 = 线性规划(对偶视角):
Maximize: d[t] - d[s]
Subject to: d[v] - d[u] ≤ w(u, v) (三角不等式)
d[s] = 0
这就是 Bellman-Ford 算法的线性规划解释——每个松弛步骤对应一个约束的"修复"。
代码实战
include/algo/linear_programming.h
#ifndef ALGO_LINEAR_PROGRAMMING_H_
#define ALGO_LINEAR_PROGRAMMING_H_
#include <vector>
#include <optional>
namespace algo {
// ========== 简化版单纯形法(教学用) ==========
// 求解 Max cᵀx s.t. Ax ≤ b, x ≥ 0
// 返回 {最优值, 解向量},若无界则返回 nullopt
struct LPSolution {
double optimal_value;
std::vector<double> x; // 最优解向量
};
std::optional<LPSolution> Simplex(
const std::vector<std::vector<double>>& A, // 约束矩阵
const std::vector<double>& b, // 右边常数
const std::vector<double>& c // 目标函数系数
);
// ========== 对偶构造 ==========
// 给出原问题 (Max cᵀx, Ax≤b, x≥0),返回对偶问题 (Min bᵀy, Aᵀy≥c, y≥0)
struct DualProblem {
std::vector<std::vector<double>> A_transpose;
std::vector<double> c; // 对偶问题的 c(即原问题的 b)
std::vector<double> b; // 对偶问题的 b(即原问题的 c)
};
DualProblem ConstructDual(const std::vector<std::vector<double>>& A,
const std::vector<double>& b,
const std::vector<double>& c);
} // namespace algo
#endif // ALGO_LINEAR_PROGRAMMING_H_
** 教学说明:** 单纯形法的完整实现约 150+ 行,涉及矩阵操作和数值稳定性处理。生产环境应使用专门的优化库(如 GLPK、Gurobi、COIN-OR)。本章提供的
Simplex接口供教学参考——完整实现作为 ⭐⭐⭐ 挑战练习,骨架代码位于include/algo/linear_programming.h。
本章小结
- 线性规划是优化线性目标函数、受线性不等式约束的通用框架
- 单纯形法沿凸多面体的边从顶点跳到更优顶点
- 强对偶定理:原问题最优值 = 对偶问题最优值
- 最大流、最短路都是线性规划的特例(约束矩阵的全幺模性保证整数解)
- 整数规划(x_i ∈ Z)是 NP 难的
关键术语
| 术语 | 释义 |
|---|---|
| 单纯形法 | 从顶点到相邻更优顶点的迭代优化算法,实践中非常高效 |
| 对偶性 | 每个 LP 有对偶问题,弱对偶给出上下界,强对偶保证最优值相等 |
| 全幺模 | 约束矩阵的性质,保证所有顶点是整数——最大流问题的整数性来源 |

浙公网安备 33010602011771号