斐波那契数列(Fibonacci sequence),又称黄金分割数列、因数学家莱昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,
// 斐波那契的案例
// 兔子数:
// 月份:1 2 3 4 5 6 7 8 9 10 11
// 数量:1 1 2 3 5 8 13 21 34 55 89
复制代码
青铜
function fib(n){
if(n === 1 || n === 2) return 1
return fib(n - 1) + fib(n - 2)
}
复制代码
白银(提高性能)
看一下性能怎么样?
let count = 0 // 计数 : 统计fib函数递归了多少次
function fib(n){
count++
if(n === 1 || n === 2) return 1
return fib(n - 1) + fib(n - 2)
}
console.log(fib(40)); // 102334155
console.log(count); // 204668309
复制代码
我去计算到40个月的时候count居然跑了204668309次,怎么办?
// 缓存: (cache), 是个存储数据的容器
// 在js里面如何去实现缓存: 用数组或对象来做缓存容器,使用键来存值,还可以通过键来取值,
// 可以实现即能存储值还可以取值。
// 使用缓存的思路:
// 1. 不是先去计算结果,先判断缓存中是否有对应的数据
// 2. 如果有,直接取出缓存中的对应数据
// 3. 如果没有,就去先计算,把计算结果存储到缓存中,方便下次使用
// 在这个案例中,如何去使用缓存
let cache = {
// 把月份作为键,把月份对应的数量作为值
// 1: 1,
// 2: 1,
// 3: 2,
// 4: 3
};
// 使用递归去计算兔子第10个月的数量
let count = 0 // 计数 : 统计fib函数递归了多少次
function fib(n){
count++
// 求出第n个月的兔子数量
// 2. 结束
if(n === 1 || n === 2) return 1
if (cache[n]) {
// 说明了: 缓存中有该月份的数量值,直接取出缓存中的数据
return cache[n]
} else {
// 说明缓存中没有该月份的数量
let ret = fib(n - 1) + fib(n - 2) // 计算的结果
// 把结果存储到缓存中
cache[n] = ret
// 把结果给返回出去
return ret
}
}
console.log(fib(40));
console.log(count);
// 月份 优化前的次数 优化后的次数
// n = 10; count = 109 17
// n = 11; count = 177 19
// n = 12; count = 287 21
// n = 20; count = 13529
// n = 21; count = 21891
// n = 40; count = 204668309 77
复制代码
黄金(安全问题)
思考cache 暴露在全局中,如果有人修改了怎么办?
// 使用闭包来保护cache数据的安全
function outer(){
// 在这个案例中,如何去使用缓存
// 缓存里面的数据是计算得出的,我们应该充分信赖缓存里面的数据
// 缓存里面的数据应该是绝对正确而且是不能被随意修改的
let cache = {
// 把月份作为键,把月份对应的数量作为值
// 1: 1,
// 2: 1,
// 3: 2,
// 4: 3
};
// 使用递归去计算兔子第10个月的数量
function fib(n){
// 求出第n个月的兔子数量
// 2. 结束
if(n === 1 || n === 2) return 1
if (cache[n]) {
// 说明了: 缓存中有该月份的数量值,直接取出缓存中的数据
return cache[n]
}
return cache[n] = fib(n - 1) + fib(n - 2)
}
return fib
}
let ret = outer()
console.log(ret(40))
复制代码
总结
对于斐波那契案例使用到的技术点:
递归: 计算结果
缓存: 存储计算的数据
闭包: 保护缓存数据的安全