动态规划:暴力递归如何变优雅?

动态规划:暴力递归如何变优雅?

斐波那契数列用纯递归算 fib(50),宇宙毁灭都算不完;加一行"记忆化",瞬间完成。这就是动态规划(DP)的魔力:把指数级暴力变成多项式级优雅。今天用最经典的例子讲透 DP 的思维框架。

一、先看暴力递归的代价

python 复制代码
def fib(n):
    if n <= 1: return n
    return fib(n-1) + fib(n-2)   # 重复计算!

fib(5) 要算 fib(4)+fib(3),fib(4) 又要算 fib(3)+fib(2)------同一个子问题被反复计算,调用树呈指数级膨胀:

复制代码
fib(20) 纯递归调用次数: 21,891 次
fib(30): 约 270 万次
fib(50): 约 200 亿亿次(算不完)

二、DP 的核心洞察:记住算过的

观察发现子问题大量重复(fib(3) 被算了 N 次)。解法朴素得惊人------算过就存下来(记忆化)

python 复制代码
memo = {}
def fib_memo(n):
    if n <= 1: return n
    if n not in memo:
        memo[n] = fib_memo(n-1) + fib_memo(n-2)
    return memo[n]

每个子问题只算一次 → 调用次数从指数级降到线性。

三、DP 的思维框架:四步法

  1. 定义状态:dpi 表示什么?(如"第 i 个斐波那契数")
  2. 找转移方程:dpi 怎么由更小的状态推出?(dpi = dpi-1+dpi-2
  3. 确定初始条件:dp0、dp1 是什么?
  4. 决定计算顺序:自顶向下(记忆化递归)或自底向上(迭代填表)

自底向上版:

python 复制代码
def fib_dp(n):
    dp = [0]*(n+1)
    dp[1] = 1
    for i in range(2, n+1):
        dp[i] = dp[i-1] + dp[i-2]
    return dp[n]

四、代码演示:561 倍的差距

python 复制代码
counts = {"recur": 0, "memo": 0}

def fib_recur(n):
    counts["recur"] += 1
    if n <= 1: return n
    return fib_recur(n-1) + fib_recur(n-2)

memo = {}
def fib_memo(n):
    counts["memo"] += 1
    if n <= 1: return n
    if n not in memo:
        memo[n] = fib_memo(n-1) + fib_memo(n-2)
    return memo[n]

fib_recur(20)
fib_memo(20)
print(f"fib(20) 纯递归调用次数: {counts['recur']:,}")
print(f"fib(20) 记忆化调用次数: {counts['memo']}")
print(f"加速: {counts['recur']/counts['memo']:.0f} 倍(指数级 -> 线性)")

运行输出:

复制代码
fib(20) 纯递归调用次数: 21,891
fib(20) 记忆化调用次数: 39
加速: 561 倍(指数级 -> 线性)

同一个 fib(20),纯递归算了 21891 次,记忆化只算 39 次------561 倍差距,n 越大差距越恐怖。这就是"记住算过的"的威力。

五、避坑清单

  1. 先想清楚状态定义再写码:状态定义错了,转移方程全是错的
  2. 区分记忆化递归和迭代 DP:前者自顶向下(适合思路直观),后者自底向上(无递归栈开销)
  3. 别急着优化空间:先写对(二维 dp 表),再谈滚动数组优化
  4. DP 不只看一维:背包是二维、区间 DP 按长度枚举、状压 DP 用位表示集合
  5. "最优子结构 + 重叠子问题"是 DP 的适用条件:不具备就别硬套(贪心/分治可能更合适)

六、想系统学算法?

本文精选自 ima 知识号【Kruptos】《数据结构与算法详解》订阅库(第 088 期动态规划入门状态与转移、第 090 期区间 DP 与 LIS 等 100 期系统教程,从数组、链表、树到排序、递归分治、动态规划、图论,每期配可运行代码)。

📚 完整系列 100 期 + 配套代码,已在 ima 知识号发布

本文只是系列的一个切片。完整系列(100 期系统教程 + 每期可运行代码)在 ima 知识号【Kruptos】持续更新中:

  • 🗂 68+ 技术知识库:信号与系统、SDR 软件无线电、数字信号处理、操作系统、AI Agent、大模型微调......几乎覆盖全部软硬件技术栈
  • 🧠 8 款 AI 技能:系列生产、知识库管理、CMMI 受管开发、自进化 Agent 等,已在 ima 技能广场上架,即装即用
  • ✅ 全部免费订阅,后续更新自动推送

🔍 订阅方式:打开 ima(腾讯智能工作台)→ 搜索「Kruptos」→ 一键订阅。或在 ima 内直接搜索《数据结构与算法详解》等知识库名称。

💬 你面试遇到最难的 DP 题是哪道?评论区聊聊------想看背包问题还是 LIS,点赞高的安排。


作者:Kruptos(西电毕业,13 年无线通信/DSP/嵌入式科研)|原创内容,转载注明出处

相关推荐
ydd10010016 天前
正则表达式匹配
正则表达式·代理模式
LayZhangStrive18 天前
设计模式(二)装饰器模式、代理模式(待更新)
设计模式·代理模式·装饰器模式·后端开发
Lam Tang23 天前
APS 系列文章10
java·代理模式
Lam Tang23 天前
APS 系列文章 08
java·代理模式
wangjialelele23 天前
动态规划DP经典题型总结(Java、C++):路径、子数组、子序列、背包问题详解
java·c++·算法·面试·动态规划·代理模式
聪明蛋子哟23 天前
从MCP到A2A再到Proxy:Agent-工具协议的三种代理模式
代理模式
Lam Tang23 天前
APS 系列文章 09
java·代理模式
Lam Tang24 天前
APS 系列文章 01
java·代理模式
只吃不吃香菜1 个月前
Windows 下基于 YAML 规则的指定域名分流:以哔哩哔哩为例
网络·网络协议·计算机网络·网络安全·代理模式