最近看到斐波那契数列的算法,觉得挺简单的,于是深入研究了一下,发现算法其实还挺美妙的。
正常的fibonacci
一般是这么算的:
function fibonacci(n) {
if (n === 1 || n === 2) {
return 1;
}
return fibonacci(n - 1) + fibonacci(n - 2);
}
但是我在测试的时候发现,当n数值比较小的时候,这个函数性能还可以,运行时间也不慢,但是当n取30,40,60的时候,我电脑的CPU就淡定不了,这事我发现,它们所消耗的时间如下:
当计算到60的时候,耗费的时间为87.049s,不仅时间太长,而且增加了CPU的压力,感觉这个算法性能不是很好。这个算法的缺点就是会重复计算许多次相同的f(n),比如要计算f(5),将会计算两次f(3),一次f(4),要是n取值更大的话,这些数值将会重复计算多次,于是想减少重复计算。
优化方法一:利用数组标记计算过的f(n)
该方法的思路是定义一个长度为n的数组,n的每一项初始值为-1,当计算出每一个f(n)
的值时保存为a[n]
,这样下一次计算f(n)
的时候,先从数组a中查看是否存在a[n]===-1
,若arr[n] ===-1
,则计算f(n)
,反之,则取a[n]
的值。代码如下:
function fibonacci1(n) {
var vArray = new Array(n+1);
for(var index = 0;index <=n;index++){
vArray[index] = -1;
}
return calcFn(n,vArray)
}
function calcFn(n,arr) {
if (n === 1 || n === 2) {
return 1;
}
if(arr[n] ===-1){
// console.log(n)
arr[n] = calcFn((n - 1),arr) + calcFn((n - 2),arr)
}
return arr[n]
}
计算结果:以下分别是n为30,40,60的计算结果
从上图来看,相对于第一种通用的方式,这个方法计算较大数值时,耗时明显缩短了,大大提高了运算速度。
优化方法二:a[n]=a[n-1]+a[n-2]
该方法的思路是,根据此通项的计算原理,a[n]=a[n-1]+a[n-2]
,我们只需要保留a[n]
的前两项,由f(1)=f(2)=1
可知,我们可初始化一个长度为2的数组a=[1,1]
,则f(3)=a[0]+a[1]=3
,然后可将交换移动数组的的两项,比如计算完f(3)
之后,a=[1,3]
,此时的数组a代表a[0] = f(2),a[1]=f(3)
,若我要计算f(4)
,则f(4)=a[0]+a[1]
,以此类推。代码如下:
function fibonacci2(n) {
var arr = [1, 1];
for(let i = 3; i <= n; ++i) {
let temp = arr[1]
arr[1] += arr[0]
arr[0] = temp
}
return arr[1]
}
实验结果表明,此方法优化之后,计算耗时跟优化方法一差不多
总结
两种优化的方法都能缩短计算耗时,都可以避免同一数值多次计算,两者的差异:
- 方法一更容易理解,但代码不够简洁
- 方法二代码简洁,思路比较灵活
- 方法一需要定义一个长度跟n一样的数组,缺点是若n比较大,那这个数组的长度也会很大,且会保留每一项
- 方法二只需要保留n-1,n-2两项,不管n为多大,只需要定义长度为2的数组,就可实现
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。