斐波那契:从“爆栈”到“闭包”,我与递归的爱恨情仇

55 阅读3分钟

“从前有座山,山里有座庙……”
递归就像这个故事——只要你不喊停,它就永远讲下去。
而斐波那契数列,就是那个最经典的“无限套娃”案例。


🌟 开场白:谁还没被 fib(100) 卡死过?

如果你刚学编程,第一次写斐波那契函数,十有八九会这样:

function fib(n) {
  if (n <= 1) return n;
  return fib(n - 1) + fib(n - 2);
}

然后你兴冲冲地 console.log(fib(50)) ——
结果电脑风扇狂转,浏览器卡成PPT,最后弹出:“页面无响应”。

别慌,这不是你的错,是递归在“报复社会”。

今天,我们就来聊聊这个看似简单、实则暗藏玄机的斐波那契数列,以及如何用缓存+闭包把它从“时间黑洞”变成“闪电侠”。


🧠 一、递归:优雅但“烧钱”

斐波那契的定义人畜无害:

  • f(0) = 0
  • f(1) = 1
  • f(n) = f(n-1) + f(n-2)

用递归实现?简直天作之合!代码短得能塞进微信昵称。

但问题来了:它的时间复杂度是 O(2ⁿ)

想象一下,计算 fib(5) 时,你会重复计算 fib(3) 两次、fib(2) 三次……
到了 fib(100),你的电脑可能已经算到宇宙热寂了(夸张了,但差不多)。

更惨的是——调用栈爆炸
JavaScript 引擎的调用栈深度有限,通常几千层就崩了。
所以 fib(10000)?别想了,直接报错:Maximum call stack size exceeded

递归像极了爱情:看起来很美,用起来很痛。


💡 二、救星登场:记忆化缓存(Memoization)

既然重复计算是罪魁祸首,那我们就记住算过的值

思路很简单:空间换时间
用一个对象(比如 cache)把已经算过的 fib(n) 存起来,下次直接拿。

const cache = {};
function fib(n) {
  if (n in cache) return cache[n];
  if (n <= 1) {
    cache[n] = n;
    return n;
  }
  cache[n] = fib(n - 1) + fib(n - 2);
  return cache[n];
}

这下 fib(100) 秒出结果!🎉
时间复杂度从 O(2ⁿ) 降到了 O(n) ,空间复杂度 O(n),稳如老狗。

但……全局变量 cache 有点丑,还可能被污染。
有没有办法把它“藏”起来?


🔒 三、闭包魔法:IIFE 来护驾!

这时候,立即执行函数表达式(IIFE) 就派上用场了。

我们用 IIFE 创建一个私有作用域,把 cache 关在里面,只暴露 fib 函数:

const fib = (function() {
  const cache = {}; // 私有变量,外部无法访问
  return function(n) {
    if (n in cache) return cache[n];
    if (n <= 1) {
      cache[n] = n;
      return n;
    }
    cache[n] = fib(n - 1) + fib(n - 2); // 注意:这里调用的是外层的 fib
    return cache[n];
  };
})();

这就是传说中的 “闭包缓存” ——
数据安全、代码整洁、性能拉满,三位一体!

而且,因为 cache 是自由变量,每次调用 fib 都能“记住”历史,真正做到一次计算,终身受益


🎯 四、进阶思考:还有没有更快的?

当然有!比如:

  • 动态规划(DP) :从底向上迭代,O(1) 空间。
  • 矩阵快速幂:O(log n) 时间,适合超大 n。
  • Binet 公式:用数学公式直接算(但有浮点精度问题)。

但对于大多数前端场景,带缓存的递归已经足够优雅又高效。


🤓 五、彩蛋:为什么叫“斐波那契”?

其实原名叫 Leonardo of Pisa,但他爸叫 Bonacci,所以他自称 Filius Bonacci(Bonacci 之子),缩写就成了 Fibonacci

所以,“斐波那契”其实是“老邦家的儿子”……
听起来是不是瞬间接地气了?😄


✅ 总结:递归不是原罪,滥用才是

方法时间复杂度空间复杂度是否实用
普通递归O(2ⁿ)O(n)❌ 别用
记忆化递归O(n)O(n)✅ 推荐
动态规划O(n)O(1)✅ 更优
矩阵快速幂O(log n)O(1)⚠️ 复杂场景

记住:

递归是思维的利器,缓存是性能的铠甲,闭包是封装的艺术。

下次再写斐波那契,别再让电脑“算到天荒地老”啦!