斐波那契数列指的是这样一个数列: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144...... 这个数列从第3项开始,每一项都等于前两项之和。
递归
const fib1 = function(n) {
if (n > 1) return fib1(n - 1) + fib1(n - 2);
return n;
};
fib1(1000); // 我的 MacPro 电脑跑不出结果
ES6 递归
const fib1 = n => (n > 1 ? fib1(n - 1) + fib1(n - 2) : n);
fib1(1000); // 和上面一样
为什么我的电脑跑不出来结果呢?
我把 n 换成了 10, 打印了一下 fib1
函数的递归调用次数, 打印次数是177次;
我又把 n 换成了 20, 打印了一下 fib1
函数的递归调用次数, 打印次数是21891次;
那如果 n = 1000, 需要调用多少次呢? 有知道的小伙伴在评论区告诉我哈。
刚才打印的时候,我发现有很多重复的调用, 比如 fib(1)
可能会调用很多次, 那么我们是否能缓存一下fib(1)
的值呢?
如果已经求过 fib(1)
的值了,我们会用一个 cache
变量缓存一下结果,下次调用的时候直接从缓存中取出来是不是就可以了!那么我们开始吧!
优化递归
const memozi = (_fib) => {
const cache = {};
return (n) => {
if (cache[n] === undefined) {
cache[n] = _fib(n);
}
return cache[n];
};
};
const fib2 = memozi(n => n > 1 ? fib2(n - 1) + fib2(n - 2) : n);
fib2(1000); //4.346655768693743e+208
我尝试使用缓存的方式优化了一下递归调用, 计算的结果是 4.346655768693743e+208
n = 10, 调用 11次
n = 20, 调用 21次
那么 n = 1000, 一定是调用了 1001 次。
那么,经过我们的优化之后,能快速的计算出 n = 1000的结果了, 计算耗时大概在 0.4ms
上下。
优化后的递归,计算速度已经很快了, 那么还有其他更快的方式吗?
for 循环
const fib3 = (n) => {
let n1 = 0;
let n2 = 1;
let c = n1;
for (let i = 1; i <= n; i++) {
c = n1 + n2;
n2 = n1;
n1 = c;
}
return c;
}
fib3(1000); //4.346655768693743e+208
这段代码使用了js 最基础的 for 循环实现, for 循环了 n 次, 计算耗时大概在 0.2ms
上下。
果然还是 for 循环最实用哈。
1000 次耗时 | 2000 次耗时 | 时间 | |
---|---|---|---|
递归 | — | — | — |
优化递归 | 0.4ms | 0.65ms | O(n) |
for 循环 | 0.2ms | 0.22ms | O(n) |
总结
今天我们学习了 3 种方式实现斐波那契数列求解的方式
-
递归
调用次数太多,性能问题严重, 不适合实际使用, 但是一种理解递归易懂的方式。
-
优化递归
缓存已调用的递归函数, 下次使用时, 直接返回递归值的结果, 性能不错。
-
for 循环
使用变量赋值累加的方式, 把 n 赋值给 n - 1, 把 n - 1 赋值给 n - 2,遍历 n 次。
如果还有小伙伴知道比 for 循环更快的计算方式, 可以在下方的评论区跟我讨论哦。