使用闭包构建记忆函数解决递归爆栈问题

202 阅读2分钟

通过记忆函数解决斐波那契数列问题

使用递归解决斐波那契数列问题时,会有着许多的重复计算,导致递归深度很快增加,就很可能会导致栈溢出。这样一个简单的递归函数的话仅仅是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)
        }
    }
}

这个记忆函数的工作原理如下:

  1. 参数检查: 首先,检查传入的参数 f 是否是一个函数,如果不是,则直接返回。
  2. 缓存对象: 创建一个对象 cache 用于缓存计算结果,这是一个闭包中的自由变量,因此在函数调用之间保留其状态。
  3. 返回函数: 返回一个新的函数,这个函数将用于替代原始函数。这个函数接受任意数量的参数。
  4. 生成唯一键: 使用参数的数量和参数值生成一个唯一的键,以标识不同的参数组合。这里使用 arguments.length + Array.prototype.join.call(arguments, ',') 来生成键。
  5. 检查缓存: 检查缓存对象中是否已经有了这个键,如果有,直接返回缓存的结果。
  6. 调用原始函数: 如果缓存中没有相应的计算结果,则调用原始函数 f.apply(this, arguments) 来计算结果。
  7. 缓存结果: 将计算结果缓存到 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也能很轻松的运行出结果。

QQ图片20231128235331.png