线性规划 — 从零精通算法与数据结构——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


本章小结

  1. 线性规划是优化线性目标函数、受线性不等式约束的通用框架
  2. 单纯形法沿凸多面体的边从顶点跳到更优顶点
  3. 强对偶定理:原问题最优值 = 对偶问题最优值
  4. 最大流、最短路都是线性规划的特例(约束矩阵的全幺模性保证整数解)
  5. 整数规划(x_i ∈ Z)是 NP 难的

关键术语

术语 释义
单纯形法 从顶点到相邻更优顶点的迭代优化算法,实践中非常高效
对偶性 每个 LP 有对偶问题,弱对偶给出上下界,强对偶保证最优值相等
全幺模 约束矩阵的性质,保证所有顶点是整数——最大流问题的整数性来源
posted @ 2026-06-22 01:09  Yobeeo  阅读(11)  评论(0)    收藏  举报