[英]Understanding a memoized Fibonacci function
function,取自这里:
function fib(n,memo) {
memo = memo || {}
if (memo[n]) {
return memo[n]
}
if (n <= 1) {
return 1
}
return memo[n] = fib(n - 1, memo) + fib(n - 2, memo)
}
这是一个有效的记忆递归 function,它接受参数 n 并返回第 n 个斐波那契数。
我无法理解的是,如何在不同的递归 function 调用中看到带有赋值的备忘录 object? 这是一张我认为正确描述了使用参数 4 调用 function 时发生的情况的图片,但如果不是,请纠正我:
记下图中标有 1. 和 2. 的 function 调用及其各自的注释。 如何为数字 2 (fib(1)) 提供一个包含在数字 1 (fib(2)) 中分配的值的备忘录参数? 提供给数字 2 (fib(1)) 的备忘录不应该与提供给它上面的 fib(3) 调用的备忘录相同,这将是一个空的 object?
我知道备忘录 object 可能只是一个全局变量,并且对它的所有分配都会在所有递归中自动可见,但我只是想了解这种特殊技术的工作原理。
希望这个描述是有道理的。 谢谢你。
您在评论中说传递值描述有助于澄清这一点。 但基本的一点是,在递归调用中,缓存是从外部传递给它们的。 他们获得对共享值的引用的副本。 初始调用接受一个缓存,如果没有提供,则创建一个新的。
如果你愿意,你可以这样称呼它:
const fibCache = {};
fib (5, fibCache) //=> 8
fib (7, fibCache) //=> 21
并且您不会在第二次调用中重新计算fib (2)
、 fib (3)
、 fib (4)
或fib (5)
的值。 但是不得不保留它是很烦人的。 下面我们创建一个帮助程序 function,它允许我们创建具有永久缓存的记忆函数。
这可能被认为是较低级别的记忆。 function 已被记忆,但仅限于初始调用的长度和它生成的所有递归调用。 有一些方法可以记忆函数,以便缓存在调用之间持续存在。
如果我们从这样的非记忆版本开始:
function fib1 (n) { if (n <= 1) { return 1 } console.log (`Calculating fib1 (${n})`) return fib1 (n - 1) + fib1 (n - 2) } console.log (`fib1 (5) returns ${fib1 (5)}`) console.log ('------') console.log (`fib1 (5) returns ${fib1 (5)}`)
我们会像这样得到 output:
Calculating fib1 (5)
Calculating fib1 (4)
Calculating fib1 (3)
Calculating fib1 (2)
Calculating fib1 (2)
Calculating fib1 (3)
Calculating fib1 (2)
fib1 (5) returns 8
------
Calculating fib1 (5)
Calculating fib1 (4)
Calculating fib1 (3)
Calculating fib1 (2)
Calculating fib1 (2)
Calculating fib1 (3)
Calculating fib1 (2)
fib1 (5) returns 8
显然,当我们尝试更大的值时会遇到问题。
使用您提供的版本:
function fib2 (n,memo) { memo = memo || {} if (memo[n]) { return memo[n] } if (n <= 1) { return 1 } console.log (`Calculating fib2 (${n})`) return memo[n] = fib2 (n - 1, memo) + fib2 (n - 2, memo) } console.log (`fib2 (5) returns ${fib2 (5)}`) console.log ('------') console.log (`fib2 (5) returns ${fib2 (5)}`)
我们减少了这一点,以便不再重复内部调用,但在外部调用之间,我们仍然重复:
Calculating fib2 (5)
Calculating fib2 (4)
Calculating fib2 (3)
Calculating fib2 (2)
fib2 (5) returns 8
------
Calculating fib2 (5)
Calculating fib2 (4)
Calculating fib2 (3)
Calculating fib2 (2)
fib2 (5) returns 8
如果我们可以将缓存存储在 function 调用之外的某个位置,我们可以制作一个完全不重复的版本:
const memoize = (fn) => { const cache = {} return function (x) { if (.(x in cache)) { cache [x] = fn (x) } return cache [x] } } const fib3 = memoize (function (n) { if (n <= 1) { return 1 } console.log (`Calculating fib3 (${n})`) return fib3 (n - 1) + fib3 (n - 2) }) console.log (`fib3 (5) returns ${fib3 (5)}`) console.log ('------') console .log (`fib3 (5) returns ${fib3 (5)}`)
Calculating fib3 (5)
Calculating fib3 (4)
Calculating fib3 (3)
Calculating fib3 (2)
fib3 (5) returns 8
------
fib3 (5) returns 8
在这里,我们将缓存存储在调用memoize
助手生成的闭包中。 对于许多问题,这是一个更令人满意的版本。 这也适用于没有递归但计算很耗时的情况。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.