Skip to content

动态规划 = 全局最优的逆向求解 ​

解决什么问题:如何把一个复杂的多步决策问题分解为简单的子问题—— 不是逐步求解,而是从终点倒推,找到全局最优。

业务问题 ​

真实场景 ​

场景 1:最短路径算法

业务:导航系统的最短路径

问题:
- 从 A 到 B 有 N 条路径
- 直接尝试所有路径太慢
- 需要高效的算法

动态规划解法:
- 从终点 B 倒推
- 每个节点到 B 的最短距离 = 子问题
- 子问题解 = 全局最优

场景 2:资源分配

业务:有限的资源如何分配

问题:
- 总资源 R = 1000
- N 个项目,每个需要不同资源
- 最大化总收益

动态规划解法:
- 状态 = 已分配的资源
- 决策 = 给哪个项目分配多少
- 从少资源到多资源逐步求解

场景 3:编辑距离

业务:两个文本的相似度

问题:
- 字符串 A 转字符串 B 的最少操作
- 操作 = 增 / 删 / 改

动态规划解法:
- 子问题 = A[i:] 转 B[j:] 的最少操作
- 从末尾倒推
- 得到全局最优

失败的典型做法 ​

做法 1:贪心算法

✗ 每步都选当前最优
✗ 不考虑全局

后果:局部最优 ≠ 全局最优

做法 2:穷举

✗ 尝试所有可能
✗ 时间复杂度爆炸

后果:不可行

做法 3:递推(无记忆)

✗ 直接递推
✗ 重复计算子问题

后果:效率低

控制论视角 ​

数学原型 ​

钱学森《工程控制论》Ch 9 §9 引言:

"动态规划是求解多步决策问题的有力工具——它把原问题分解为重叠的子问题,通过逆向求解(从终点倒推),利用最优性原理,得到全局最优解。"

控制论解法 ​

动态规划的核心:

状态:描述系统在某一时刻的特征
决策:从一个状态到另一个状态的选择
转移:决策带来的状态变化
代价:决策的代价 / 收益

最优性原理:
- 整体最优 ⟺ 局部最优
- 子问题的最优 = 全局最优的一部分

逆向求解:
- 从终点开始
- 每个状态的最优值 = 子问题最优值 + 当前决策
- 逐步倒推

与业务的对应 ​

控制论概念软件工程
多步决策多阶段决策
状态系统的中间状态
决策每个阶段的选择
代价收益 / 成本
最优性原理子问题独立
逆向求解倒序计算

核心概念 ​

定义 ​

动态规划:把复杂的多步决策问题分解为重叠的子问题,通过最优性原理和逆向求解,得到全局最优解。

软件工程对位:复杂决策问题的分解与求解。

动态规划的四个要素 ​

1. 状态:
   - 描述系统在某一时刻的特征
   - 例:剩余资源、当前位置、已分配量

2. 决策:
   - 从一个状态到另一个状态的选择
   - 例:分配多少资源、走哪条路径

3. 转移:
   - 决策带来的状态变化
   - 例:分配后剩余资源 = 总 - 已分配

4. 代价 / 收益:
   - 决策的代价 / 收益
   - 例:分配资源的收益、走路径的距离

关键属性 ​

属性含义
最优子结构整体最优 = 子问题最优 + 当前决策
重叠子问题子问题被多次使用
状态转移状态之间的转换规则
逆向求解从终点倒推
记忆化缓存子问题结果

动态规划 vs 贪心 vs 穷举 ​

方法时间复杂度适用场景
动态规划多项式有最优子结构
贪心多项式局部最优 = 全局最优
穷举指数规模小
递归指数无重叠子问题

工程实践 ​

步骤 1:识别问题是否适合动态规划 ​

判断标准:

1. 有最优子结构?
   - 整体最优 = 子问题最优 + 当前决策
   - 例:最短路径、编辑距离

2. 有重叠子问题?
   - 同样的子问题被多次求解
   - 例:Dijkstra、Knapsack

3. 状态可枚举?
   - 状态空间有限
   - 可以存储所有状态

→ 都满足 = 适合动态规划

步骤 2:定义状态 ​

状态定义:

- 状态的维度(如位置、资源、时间)
- 状态的取值范围
- 状态的存储方式

例(最短路径):
- 状态 = (当前位置, 已走路程)
- 状态空间 = N × 最大距离
- 存储 = 二维数组

例(资源分配):
- 状态 = (当前项目, 已分配资源)
- 状态空间 = N × R
- 存储 = 二维数组

步骤 3:定义决策和转移 ​

决策和转移:

- 决策空间 = 所有可能的选择
- 状态转移 = (当前状态, 决策) → 下一状态

例(最短路径):
- 决策 = 选择下一步的城市
- 转移 = (当前位置, 决策) → (下一位置, 路程+边权重)

例(资源分配):
- 决策 = 给当前项目分配多少资源
- 转移 = (项目索引, 已分配) → (项目索引+1, 已分配+分配量)

步骤 4:逆向求解 ​

逆向求解步骤:

1. 初始化:
   - 终点状态 = 已知最优值(通常是 0)
   - 其他状态 = 未知

2. 状态转移:
   - 对每个状态,计算最优决策
   - V(状态) = min/max (代价(决策) + V(下一状态))

3. 倒推:
   - 从已知状态推未知状态
   - 直到所有状态都有值

4. 路径回溯:
   - 从起点出发
   - 根据 V 选择最优决策

步骤 5:实现 + 优化 ​

实现:

1. 简单递归 + 记忆化
2. 自底向上 DP 表
3. 空间优化(滚动数组)

优化:
- 时间复杂度:从 O(2^n) 降到 O(n^2)
- 空间复杂度:从 O(n^2) 降到 O(n)

常见反模式 ​

反模式 1:贪心但不适合 ​

症状:
- 用贪心算法
- 但局部最优 ≠ 全局最优

后果:错误结果
正解:用动态规划

反模式 2:状态定义错误 ​

症状:
- 状态不够精细
- 遗漏关键信息

后果:算法错误
正解:仔细设计状态

反模式 3:复杂度爆炸 ​

症状:
- 状态空间太大
- 时间 / 空间复杂度爆炸

后果:不可行
正解:状态压缩 / 剪枝

工程实例 ​

实例 1:调度优化 ​

业务:N 个任务,单机调度,最大化收益

状态:
- dp[i][t] = 处理完前 i 个任务、耗时 t 的最大收益

转移:
- 不做任务 i:dp[i][t] = dp[i-1][t]
- 做任务 i:dp[i][t] = dp[i-1][t-time[i]] + profit[i]

求解:
- 从 dp[0][0] = 0 开始
- 逐步递推
- 得到最优解

应用:
- 任务调度
- 资源分配
- 投资组合

实例 2:库存管理 ​

业务:库存的最优补货策略

状态:
- 当前库存 = s
- 未来需求 = d

转移:
- 补货量 = u
- 下一期库存 = s + u - d

代价:
- 库存成本 + 缺货成本 + 补货成本

动态规划:
- 找到最优补货策略
- 最小化总成本

实例 3:A/B 实验的多步决策 ​

业务:分阶段推出新功能

状态:
- 当前阶段 = s(阶段 1, 2, 3)
- 当前用户覆盖 = c

转移:
- 扩大覆盖 = c + Δ
- 维持覆盖 = c

代价:
- 收益:覆盖增加带来的业务价值
- 风险:覆盖增加带来的故障成本

动态规划:
- 找到最优推出节奏
- 最大化收益 / 风险比

性能指标 ​

动态规划健康指标 ​

指标设计目标衡量方法
算法正确性100%已知答案对比
时间复杂度多项式计算时间
空间复杂度合理内存使用
最优解偏差0与理论最优对比

监控设计 ​

关键监控:
- 算法执行时间
- 内存使用
- 解的质量(与历史对比)

告警分级:
- P2:执行时间 > 设计目标 50%
- P1:执行时间 > 设计目标 100%
- P0:算法错误 / 解质量差

边界判断 ​

何时不用 ​

场景 1:无最优子结构

- 整体最优 ≠ 子问题最优 + 当前决策
- 不适合动态规划

→ 用其他方法

场景 2:状态空间太大

- 状态数过多
- 时间 / 空间爆炸

→ 用近似算法

场景 3:贪心足够

- 局部最优 = 全局最优
- 动态规划过度设计

→ 用贪心

与其他方法论的边界 ​

相邻概念区别
自寻最优点自寻最优点关注"无模型搜索";本方法论关注"已知模型的最优"
极大值原理极大值原理关注"连续最优控制";本方法论关注"离散多步决策"
递归递归无记忆化;动态规划有记忆化

何时升级(更复杂的优化) ​

当以下情况出现时,考虑升级:

1. 状态空间太大 → 状态压缩
2. 多目标优化 → 多目标 DP
3. 不确定性 → 随机 DP
4. 大规模问题 → 分布式 DP

关键洞察 ​

钱学森 Ch 9:动态规划是求解多步决策问题的核心方法。

软件工程的优化智慧:

不是所有问题都需要全局搜索——很多问题可以通过动态规划分而治之。

应用场景:

  • 路径规划 → Dijkstra、Floyd
  • 资源分配 → 背包问题变种
  • 文本相似度 → 编辑距离
  • 生物信息 → 序列比对
  • 金融工程 → 期权定价

好的动态规划 = 正确的状态 + 正确的转移 + 正确的优化

相关链接 ​

IDDD 对位 ​

核心:动态规划 = 跨阶段最优决策——IDDD Saga 编排和 cybernetics DP 同构。

从名家方法论与工程化思路中蒸馏出自己的工程体系。