斐波拉切数列
指的是这样一个数列:0、1、1、2、3、5、8、13、21、34...,F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2).
在javascript中的计算方法有
- 递归
// 执行次数
let count = 0;
function fib(n) {
// 每次调用均count++
count++;
if (n === 0) {
return 0n
}
if (n === 1) {
return 1n
}
return fib(n - 1) + fib(n - 2)
}
// 开始时间
console.time()
console.log(fib(45))
// 结束时间,可以计算程序运行时间
console.timeEnd()
console.log(count)
console.log(memory)
执行结果:
以上是递归的方式,是呈指数级的增长,主要是因为其中包含很多重复的计算
- 记忆化搜索:自顶向下的思路
// 执行次数
let count = 0;
// 记忆化的数组
let memory = {};
function fib(n) {
// 每次调用均count++
count++;
// 初始化初值,n表示的是BigInt数据类型
if (n == 0) {
return 0n;
}
if (n == 1) {
return 1n;
}
if (!memory[n]) {
memory[n] = fib(n - 1) + fib(n - 2)
}
// 返回计算结果
return memory[n]
}
// 开始时间
console.time()
console.log(fib(45))
// 结束时间,可以计算程序运行时间
console.timeEnd()
console.log(count)
console.log(memory)
执行结果
以上是自顶向下的方式,时间复杂度为O(n),计算了45 * 2 - 1 = 89次。
- 动态规划:自底向上的思路
// 执行次数
let count = 0;
// 记忆化的数组
let memory = {};
function fib(n) {
// 初始化初值,n表示的是BigInt数据类型
memory[0] = 0n;
memory[1] = 1n;
// 从第三个数据开始,均是前两个的加和
for (let i = 2; i <= n; i++) {
// 每次调用均count++
count++;
memory[i] = memory[i - 1] + memory[i - 2]
}
// 返回计算结果
return memory[n];
}
// 开始时间
console.time()
console.log(fib(45))
// 结束时间,可以计算程序运行时间
console.timeEnd()
console.log(count)
console.log(memory)
执行结果
以上是底向上的方式,时间复杂度也为O(n),仅仅计算了45 - 1 = 44次。
动态规划
将原问题拆解成若干子问题,同时保存子问题的答案,使得每个子问题只求解一次,最终获得源问题答案。
总结:
从递归,到记忆化搜索,最后使用动态规划,是一个逐渐优化的过程。