斐波那契數列


  斐波那契數列是一組非常有規律的數列,如下所示

  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個數的和。后面的數,永遠是它前面兩個數的和。數學表示,就是
   F 0 = 0, F 1=1, F 2 = F 1 + F 0,   F n = F n-1 + F n-2
  那現在求第n個斐波那契數,比如第9個斐波那契數是多少,那怎么辦
  1,遞歸
  最先想到的是遞歸,根據公式 F n = F n-1 + F n-2,求F n,就要先求F n-1 和 F n-2, F n-1 就要求F n-2 和F n-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,這時直接返回值,就可以了,因為F 0 = 0, F 1=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;
}

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM