斐波那契数列

  斐波那契数列是一组非常有规律的数列,如下所示

  0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55 .....

  第0个数是0,第1个数是1,第2个数是第1个数和第0个数相加的和(1+0),第3个数是第2个数和第1个数相加的和(1+1),依次类推,第n个数永远都是第n-1个数 和第n-2个数的和。后面的数,永远是它前面两个数的和。数学表示,就是
   F0 = 0, F1=1, F2 = F1 + F0,   Fn = Fn-1 + Fn-2
  那现在求第n个斐波那契数,比如第9个斐波那契数是多少,那怎么办
  1,递归
  最先想到的是递归,根据公式 Fn = Fn-1 + Fn-2,求Fn,就要先求Fn-1 和 Fn-2, Fn-1 就要求Fn-2 和Fn-3,如果定义一个函数fib, 它接受n作为参数,返回第n个斐波那契数, 那么 fib(n) 就要返回fib(n-1) and fib(n-2), fib(n-1),fib(n-2) 正好调用自己,fib(n-1) 正好返回 fib(n-2) + fib(n-3) 函数不停地调用自己,但调用的参数值却是不断在减小,n, n-1, n-2, n-3,最终参数会到0,1,这时直接返回值,就可以了,因为F0 = 0, F1=1
function fib1(n) {
    if(n === 0) {
        return 0;
    }

    if (n === 1) {
        return 1;
    }

    return fib1(n - 1) + fib1(n -2);
}

   但是递归实现有一个问题,就是存在大量的重复计算

                 fib(5)   
                     /                \
               fib(4)                fib(3)   
             /        \              /       \ 
         fib(3)      fib(2)         fib(2)   fib(1)
        /    \       /    \        /      \
  fib(2)   fib(1)  fib(1) fib(0) fib(1) fib(0)
  /     \
fib(1) fib(0)
  其实可以从前向后看,知道第0个数和第1个数,就可以知道第2个数,知道第2个数,又知道第1个数,就可以知道出第3个数,知道某个数, 又知道它的前一个数,就可以知道它后一个数,知道某个数之后,可以把它存起来,和它前面一个数,进行计算,就可以知道后一个数,然后后一个数再存起来,和前面的数进行计算,就可以知道后后一个数,依次类推,就可以知道第n个数。知道某个数,然后把它存起来,并计算下一个数,有两种实现方式,一种是数组,把知道的某一个数作数组的每一项存起来。一种是声明变量,把以前计算的数用变量存起来。
  2,数组
  斐波那契数列的第0个数和第1个数是知道的,分别是0和1,那就声明一个数组arr,  数组第0项是0,arr[0] = 0,第1项是1,arr[1] = 1; 因为知道第1个数和第0个数,可以知道第2个数,所以知道数组的第0项和第1项, 就可以知道第2项(第0项+第1项)  arr[2] = arr[1] + arr[0],  知道第2个数,就知道第3个数(第2个数+第1个数),也就意味着知道arr[2], 就可以计算出 arr[3] = arr[2] + arr[1], 同理,知道arr[3], 又可以计算arr[4], 直到arr[n]。可以发现,斐波那契数列的每一个数对应着数组的每一项。第n个斐波那契数就是数组的第n项。现在就变成了知道arr[0], arr[1], 求arr[n], 公式应该是arr[n] = arr[n-1] + arr[n-2] 。可以使用循环,从第2项开始,直接循环到n,循环体就是arr[n] = arr[n-1] + arr[n-2]。那arr[n] 就是第n个斐波那契数,声明一个 n+1 长度的数组。
function fib2(n) {
    if (n == 0) {
        return 0;
    }

    if (n == 1) {
        return 1;
    }
    
    let arr = new Array(n + 1);

    arr[0] = 0;
    arr[1] = 1;

    for (let index = 2; index <= n; index++) {
        arr[index] = arr[index -1] + arr[index-2]; 
    }
    return arr[n];
}

   3,使用变量

  使用数组有点浪费空间了,实际上,求斐波那契数中的某个数,只要知道它的前面第一个数和前面的第二个数就可以了,也就是说,在计算的过程中,只要保留前面第一个数和前面第二个数就可以了,没有必要把前面所有的数都用数组保存起来。

   现在知道第0个数是0, 第1个数是1, 可以把第1个数看做是前面第一个数,把第0个数看做是前面第二个数, 前面两个数都知道了,那就可以计算出一个斐波那契数,这个斐波那契数就是第2个数1+0。此时,如果把计算得出的第2个数看成是前面第一个数,前面第二个数是知道的(就是第1个数(1))那就可以计算出第3个斐波那契数1+1。 此时,如果把计算得出的第3个数看成是前一个数,前面第二个数也是知道的(就是第2个数 1),那就可以计算出第4个数(2+1)。可以看到求斐波那契数,就是不停的调换前面的第一个数和前面的第二个数。现在可以用代码实现一下
  声明两个变量,preFirst, preSecond 表示前面一个数和前面第二个数,求出的斐波那契数设为curFib,  第一次的时候,
let preFirst = 1;
let preSecond = 0;
let curFib = preFirst + preSecond; // 1 + 0 = 1 此时curFib 是第2个数

  第二次的时候,第一次preFirst 变成了preSecond, 把第一次中求出来的curFib 作为preFirst, 

preSecond = preFirst; // 1
preFirst = curFib; // 1 curFib = preFirst + preSecond; // 1 + 1 = 2 curFib 是第3个数

  第三次的时候,就是重复第二次的动作,第二次中的preFirst 变成preSecond, 把第二次计算出来的结果curFib作为preFirst,

preSecond = preFirst; // 1
preFirst = curFib; // 2 curFib = preFirst + preSecond; // 2 + 1 = 3 curFib 是第4个数

  重复意味着循环,第二次开始循环,它求出来的是第3个数,第三次循环,它求出来的是第4个数,那么第n-1次循环,可以求出第n个数

function fib3(n) {
    let preFirst = 1;
    let preSecond = 0;
    let curFib = preFirst + preSecond;

    for (let i = 2; i <= n-1; i++) {
        preSecond = preFirst;
        preFirst = curFib;
        curFib = preFirst + preSecond;
    }

    return curFib;
}

  当然,n=0 和n=1 的时候,直接return 0 和1 就好了。还有一种循环,就是在循环中计算斐波那契数,那循环就要从第1次开始计算,

function fib3(n) {
    if (n == 0){
        return 0;
    }
    if (n == 1){
        return 1;
    }
    let preFirst = 1;
    let preSecond = 0;
    let curFib;

    for (let i = 1; i <= n-1; i++) {
        curFib = preFirst + preSecond;
        preSecond = preFirst;
        preFirst = curFib;
    }

    return curFib;
}

 

 
posted @ 2021-06-09 18:25  SamWeb  阅读(3419)  评论(0编辑  收藏  举报