通过记忆函数解决斐波那契数列问题
使用递归解决斐波那契数列问题时,会有着许多的重复计算,导致递归深度很快增加,就很可能会导致栈溢出。这样一个简单的递归函数的话仅仅是n=50的情况就很难运行出结果。
var fibonacci =function(n){
return n < 2? n : fibonacci(n - 1) + fibonacci(n - 2);
}
而通过使用闭包构造的记忆函数,通过缓存之前计算的结果,避免了重复的递归调用,从而降低了栈的深度。
// 记忆函数模板
function memorize(f){
if(typeof f !== 'function')return ;
var cache = {}// 负责空间换时间 不会被销毁的 自由变量
return function(){
var key = arguments.length + Array.prototype.join.call(arguments,',')
// add
if(key in cache){
return cache[key]
}else{
return cache[key] = f.apply(this,arguments)
}
}
}
这个记忆函数的工作原理如下:
- 参数检查: 首先,检查传入的参数
f是否是一个函数,如果不是,则直接返回。 - 缓存对象: 创建一个对象
cache用于缓存计算结果,这是一个闭包中的自由变量,因此在函数调用之间保留其状态。 - 返回函数: 返回一个新的函数,这个函数将用于替代原始函数。这个函数接受任意数量的参数。
- 生成唯一键: 使用参数的数量和参数值生成一个唯一的键,以标识不同的参数组合。这里使用
arguments.length + Array.prototype.join.call(arguments, ',')来生成键。 - 检查缓存: 检查缓存对象中是否已经有了这个键,如果有,直接返回缓存的结果。
- 调用原始函数: 如果缓存中没有相应的计算结果,则调用原始函数
f.apply(this, arguments)来计算结果。 - 缓存结果: 将计算结果缓存到
cache中,以备将来使用。
使用这个模板来解决斐波那契数列问题。直接传入原本的递归函数fibonacci =memorize(fibonacci)重新构造递归函数。
let count = 0
var fibonacci =function(n){
count++;
return n < 2? n : fibonacci(n - 1) + fibonacci(n - 2);
}
function memorize(f){
if(typeof f !== 'function')return ;
var cache = {}// 负责空间换时间 不会被销毁的 自由变量
return function(){
var key = arguments.length + Array.prototype.join.call(arguments,',')
// add
if(key in cache){
return cache[key]
}else{
return cache[key] = f.apply(this,arguments)
}
}
}
fibonacci =memorize(fibonacci)
console.log(fibonacci(500));
这时即使是n=500也能很轻松的运行出结果。