JavaScript解斐波那契(Fibonacci)数列的实用解法

JavaScript解斐波那契(Fibonacci)数列的实用解法

我们经常会在面试题中看到如下题目:输入n,求斐波那契数列的第n项,斐波那契数列的定义如下:

F(0)=0, F(1)=1, n>1时,F(n)=F(n-1)+F(n-2)。

一种效率很低的解法

当遇到这种函数时,我们很容易的想到递归函数,解法如下:

function fibonacci(n) {
  if(n <= 1) {return n};
  else {
    return fibonacci(n-1) + fibonacci(n-2);
  }
}

这个方法确实能解决这道题目,但递归的解法并不适合这道题目,用递归解法将会有很严重的效率问题,我们以f(10)为例分析一下原因:

clipboard.png

由上述图片我们可以看出,要想求得f(10),需要先求得f(8)和f(9),同样,要想求得f(9),也要求得f(7)和f(8)。不难看出,树结构当中很多结点是重复的,而且重复的节点数会随着n的增大而急剧增大,这意味着计算量将会随着n的增大而急剧增大。事实上,递归所需的时间复杂度是以n的指数方式递增的,由此我们可以试试当n为100时需要耗费的时间会有多长,这是难以接受的。

动态规划解法

造成效率低下的主要原因就是重复计算太多,我们只要想个办法避免重复计算即可,这里有个很容易的算法:

var fib = 0,
    fib1 = 0,
    fib2 = 1;
function fibonacci(n) {
  if(n <= 1){
    return n;
  } else {
    for(var i =1; i < n; ++i) {
      fib = fib1 + fib2;
      fib1 = fib2;
      fib2 = fib;
    }
    return fib;
  }
}

理解这种方法很简单,我们只是从下往上计算,首先根据f(0)和f(1)算出f(2),再根据f(1)和f(2)算出f(3),依次类推我们就可以算出第n项了,而这种算法的时间复杂度仅为O(n),比递归函数的写法效率要大大增强。

Memoization

我们还可以将已经得到的数列中间项保存起来,若下次计算的时候我们先查找一下,若前面已经出现过则不用再重复计算了。

在JavaScript中,递归是拖慢脚本运行速度的罪魁祸首之一,太多的递归会让浏览器越来越慢乃至奔溃,这是需要我们解决的性能问题。

我们可以使用memoization技术来替代函数中太多的递归调用,memoization是一种可以缓存之前运算结果的一种技术,当在执行运算操作时,我们先从缓存对象中读取看看是否有我们需要读取的值,若有则直接从缓存对象中读取,若没有则进行计算,并将缓存结果存入缓存对象中。

下面是一个可以处理很多类型递归函数的memoizer()函数:

`function memoizer(fun, cache) {

 cache = cache || {};
 var shell = function(arg) {
   if( ! (arg in cache)) {
     cache[arg] = fun(shell, arg);
   }
   return cache[arg];
 } ;
 return shell;

}`

其中第一个参数为原有函数,第二个参数为缓存对象,是可选参数(因为并不是所有递归函数都包含初始信息)。首先将缓存对象的类型从数组转换为对象,这样就可以适用于那些不是返回整数的递归函数。使用in操作符判断参数是否已经包含在了缓存里,会比测试cache[arg]更安全些,因为undefined是一个有效的返回值(这里其实我也不太明白,弄清楚后会补上)。

接下来我们就可以调用memoizer来解这个这个问题:

var fibonacci = memoizer(function(fibon, n) {
  return fibon(n - 1) + fibon(n - 2);
}, {'0' : 0, '1' : 1});

这时我们就可以通过fibonacci(100)来执行函数,同样运用此种方法复杂度为O(n),大大优化了执行效率。

后记

个人更喜欢memoizer的解法,因为我觉得这种解法更加的优雅,运用了JavaScript函数式编程的特性,非常值得借鉴。

在我看来,函数的执行效率很重要,勿以事小而不为,平时多积累一些优秀的解法,从平时的小知识点出发,慢慢进步,假以时日终将也可以写出优雅高效率的方法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值