斐波那契問題


斐波那契數列,指的是這樣一個數列:1、1、2、3、5、8、13、21...,除第1,2位的數為1外,其他數為前兩位數字的相加之和。

1.斐波那契數列與經典兔子繁殖問題

一般而言,兔子在出生兩個月后,就有繁殖能力,一對兔子每個月能生出一對小兔子來。如果所有兔都不死,那么在有1個月大的一對兔子的條件下一年以后可以繁殖多少對兔子?

我們可以分析一下。

第一個月一對兔子A生出一對小兔B。

第二個月還是原來的一對兔子A生出一對小兔C。

第三個月A生出一對小兔D,B生出一對小兔E。

第四個月A生出一對小兔F,B生出一對小兔G,C生出一對小兔H。

依次類推......如下表所示:

月份 1 2 3 4 5 6 7 8 9 10 11 12
每個月新出生的兔子對數 1 1 2 3 5 8 13 21 34 55 89 144

 

 

一年后繁殖的兔子對數量為:1+1+2+3+5+8+13+21+34+55+89+144

2.C語言使用遞歸來實現斐波納契數列

fibonacci.h
long fibonacci(int n);
遞歸實現菲波那契數列
#include <stdio.h> #include <stdlib.h> #include "fibonacci.h"


int main(void) { int n; long result; scanf("%d",&n); result=fibonacci(n); printf("%ld",result); return EXIT_SUCCESS; } long fibonacci(int n) { if( n>0 && n<=2 ) return 1; else
        return fibonacci(n-1)+fibonacci(n-2); }

遞歸帶來的好處是使算法變得更加簡明,可讀性高。然而,使用遞歸也帶來了額外的內存開銷.可以考慮使用迭代實現斐波那契。

3.C語言使用迭代來實現斐波那契數列

迭代實現斐波那契數列
#include <stdio.h> #include <stdlib.h> #include "fibonacci.h"


int main(void) { int n; long result; scanf("%d",&n); result=fibonacci(n); printf("%ld",result); return EXIT_SUCCESS; } long fibonacci(int n) { long result,previous_result,temp; result = previous_result =1; while(n>2) { temp = result; result += previous_result; previous_result = temp; n -= 1; } return result; }

原先自己的第一個思路是使用一個long數組存放斐波那契數列,在最后的第n位再把前兩項的數字相加返。但這樣做的話會浪費掉很多用不到的數組內存,所以改進為使用3個變量,一個記錄前一個結果,一個記錄當前結果,一個做為中間的temp變量,作為一個交換的介體。

4.斐波那契查找

斐波那契數列又稱為黃金分割數列。為什么叫做黃金分割數列呢?理由是當n趨近於無窮大的時候,后一項和前一項的比值越來越接近黃金分割1.618.  3/2=1.5,5/3=1.666,8/5=1.6,13/8=1.625...

數學背景:數字0.618…更為數學家所關注,它的出現,不僅解決了許多數學難題(如:十等分、五等分圓周;求18度、36度角的正弦、余弦值等),而且還使優選法成為可能。優選法是一種求最優化問題的方法。如在煉鋼時需要加入某種化學元素來增加鋼材的強度,假設已知在每噸鋼中需加某化學元素的量在1000—2000克之間,為了求得最恰當的加入量,需要在1000克與2000克這個區間中進行試驗。通常是取區間的中點(即1500克)作試驗。然后將試驗結果分別與1000克和2000克時的實驗結果作比較,從中選取強度較高的兩點作為新的區間,再取新區間的中點做試驗,再比較端點,依次下去,直到取得最理想的結果。這種實驗法稱為對分法。但這種方法並不是最快的實驗方法,如果將實驗點取在區間的0.618處,那么實驗的次數將大大減少。這種取區間的0.618處作為試驗點的方法就是一維的優選法,也稱0.618法。實踐證明,對於一個因素的問題,用“0.618法”做16次試驗就可以完成“對分法”做2500次試驗所達到的效果。因此大畫家達·芬奇把0.618…稱為黃金數。

斐波那契查找
#include <stdio.h>
#define MAX_SIZE 30
int fibonacci(int n); int main (void) { /*准備工作*/
    int n,i,key; printf("Please input n:"); scanf("%d",&n); int list[MAX_SIZE]; printf("Please input your sorted array:"); for(i=1 ; i<=n ;i++) { scanf("%d",&list[i]); } printf("Please input the key:"); scanf("%d",&key); /*比較找到不小於n的斐波那契數的位置*/
    int k = 1; while(n>fibonacci(k)) { k++; } /*在n的個數不足找到的斐波那契數,用list【n】補齊*/
    for(i=n+1; i<=fibonacci(k); i++) { list[i]=list[n]; } /*定義key的位置*/
    int pos = -1; int low,high,mid; low = 1; high = n; while(low <= high) { mid =low + fibonacci(k-1) -1; if(key < list[mid]) { high=mid - 1; k = k-1; } else if(key > list[mid]) { low=mid + 1; k = k-2; } else { if(mid<n) { pos = mid; break; } else { pos = n; break; } } } printf("The result position is %d.",pos); return 0; } int fibonacci(int n) { int result,previous_result,temp; result = previous_result = 1; while(n>2) { temp=result; result+=previous_result; previous_result=result; n--; } return result; }

 對斐波那契查找可能存在兩個比較難理解的地方。

(1)

/*在n的個數不足找到的斐波那契數,用list【n】補齊*/
for(i=n+1; i<=fibonacci(k); i++) { list[i]=list[n]; }

由於斐波那契查找是基於斐波那契數列的查找,要求有序表的個數必須是存在於斐波那契數列中的數,所以在數不足的情況下,如n=10,fibonacci(7)=13 > 10,必須將有序表的個數擴增到等於斐波那契數,則list[11]=list[12]=list[13]=list[n]。

(2)

if(key < list[mid]) { high=mid - 1; k = k-1; } else if(key > list[mid]) { low=mid + 1; k = k-2; }

在key<list[mid] 的情況下 k=k-1,ket > list [mid] 的情況下 k=k-2。這又是為什么呢?

對於斐波那契查找,分割是從mid =low + fibonacci(k-1) -1 開始的,現在數組的長度為fibonacci(k),mid 將數組分為兩個部分,前一部分為[1,mid],長度為fiboncaai(k-1),則后一部分的長度為fibonacci(k)-fibonacci(k-1),根據斐波那契的性質,f(k) - f(k-1) = f(k-2),則后一部分的長度為fibonacci(k-2),所以當key > list[mid] 的時候 k = k - 2。

斐波那契的時間復雜度也為O(log n),相對與擇半查找進行加法與除法運算(mid=(low + high)/2 ),斐波那契查找只是最簡單加減法運算( mid = low + fibonacci(k-1) -1 ),在海量數據的查找過程中,這種細微的差別可能會影響最終的查找效率。


免責聲明!

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



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