先说一下Fibonacci数列:一个
Fibonacci数字是前2个
Fibonacci
数字之和,
Fibonacci
最前面的2个数是0和1。
不多说,大家都知道递归算法:
var fibonacci = function(n){
return n<2 ? n : fibonacci(n - 1) + fibonacci(n - 2);
}
结果:
console.info(fibonacci(1));//1
console.info(fibonacci(2));//1
console.info(fibonacci(3));//2
console.info(fibonacci(4));//3
console.info(fibonacci(5));//5
console.info(fibonacci(6));//8
这是最典型的
Fibonacci实现,但该函数在计算
fibonacci(6)的时候,它计算了25次。在总的6次取值中,共计算了58次。
在《
JavaScript语言精粹》一书中介绍了
下面函数来提高计算效率,该函数通过记忆存储结果,而大大减少计算量,总的
6次取值总共仅计算了16次。
var memorizer = function(memo,fundamental){
var shell = function(n){
var result = memo[n];
if(typeof result !== 'number'){
result = fundamental(shell,n);
memo[n] = result;
}
return result;
};
return shell;
}
var fibonacci = memorizer([0,1],function(shell,n){
return shell(n-1)+shell(n-2);
});
结果:
console.info(fibonacci(1));//1
console.info(fibonacci(2));//1
console.info(fibonacci(3));//2
console.info(fibonacci(4));//3
console.info(fibonacci(5));//5
console.info(fibonacci(6));//8
个人理解:
首先 memorizer返回了shell,shell是一个function,shell的参数为n,即memorizer返回了一个function(n){..}的函数
可以理解为下面的语句:
fibonacci = function(n){
var result = memo[n];
if(typeof result !== 'number'){
//fibonacci就是memorizer返回的那个shell,shell和fibonacci其实是同一个东西
//所以fundamental函数的参数shell就是fibonacci
result = fundamental(shell,n);
memo[n] = result;
}
return result;
}
然后我们分析上面函数中的memo和fundamental
通过原本的代码可以看出,其中memo作为函数的入参,可以认为一个数组指针,指向memorizer的第一个参数:[0,1]
var fibonacci = memorizer
([0,1],function(shell,n){return shell(n-1)+shell(n-2);});
fundamental
作为函数的另一入参,可以认
为是一个function指针,指向memorizer的第二个参数,即匿名function:
var fibonacci = memorizer([0,1],function(shell,n){return shell(n-1)+shell(n-2);});
因为fibonacci就是memorizer返回的shell,
所以
shell和fibonacci其实是同一个东西,上面fundamental函数的参数shell即是fibonacci
fibonacci = function(n){
var result = memo[n];//memo为数组[0,1]
if(typeof result !== 'number'){
result = fundamental(<strong><span style="color:#33cc00;">fibonacci</span></strong>,n);// fundamental(shell,n) shell用fibonacci替换
memo[n] = result;
}
return result;
}
即得到以下代码:
调整后得到基本的带记忆功能的Fibonacci函数:
fibonacci = function(n){<strong>
<span style="color:#33cc00;">var memo = [0,1];</span></strong>
var result = memo[n];//memo为数组
if(typeof result !== 'number'){
//result = fundamental(fibonacci,n);
//fundamental(shell,n){return shell(n-1)+shell(n-2);}, 直接把执行语句拿出来
<span style="color:#33cc00;"><strong>result = fibonacci(n-1)+fibonacci(n-2);</strong>
</span> memo[n] = result;
}
return result;
}
测试
结果:
console.info(fibonacci(1));//1
console.info(fibonacci(2));//1
console.info(fibonacci(3));//2
console.info(fibonacci(4));//3
console.info(fibonacci(5));//5
console.info(fibonacci(6));//8
总结:memorizer介绍了一种加速程序计算的优化技术-memorization(记忆),通过缓存结果,使得函数避免重复验算已被处理的输入。
简单说来,
memorizer
返回的factorial函数会记住之前操作的结果,如果记忆数组里存在所需的数值,就从数组里拿,如果数组中不存在,才去计算,
从而避免了无谓的运算。
PS:这里另一个重点在于memorizer函数将这种形式抽象出来,将其一般化,使我们只用提供基本的公式,就能生产出其他函数的函数。
例如:要产生可记忆的阶乘函数,只需要提供基本的阶乘公式:
var memorizer = function(memo,fundamental){
var shell = function(n){
var result = memo[n];
if(typeof result !== 'number'){
result = fundamental(shell,n);
memo[n] = result;
}
return result;
};
return shell;
}
var factorial = memoizer ([ 1 , 1 ], function ( shell , n ){ return n * shell ( n - 1 );});
相关阅读:《JavaScript语言精粹》(
蝴蝶书
)第四章15节