Fibonacci(費伯納西)數列求解 zz

來源:互聯網
上載者:User

  描述了動物繁殖數量、植物花序變化等自然規律。作為一個經典的數學問題,Fibonacci數列常作為例子出現在程式設計、資料結構與演算法等多個相關學科中。

  下面簡單地分析一下常見的Fibonacci數列求解演算法。

  1、遞迴法。大多數教材在講解遞迴演算法時總喜歡以Fibonacci數列為例,這是因為我們可以直觀地從定義公式的第三行看出Fibonacci數列的遞迴性。其C++實現如下:

unsigned long Fib(int n)            {            if (n <= 1) {            return n;            } else {            return Fib(n - 1) + Fib(n - 2);            }            }

  遞迴演算法與定義公式十分吻合,容易理解,但計算過程存在大量重複的運算,時間複雜度達到了O(2^n),使用的記憶體空間也隨著函數調用棧的增長而增長。這顯然不適於實用的程式。

  2、表驅動的遞迴法。這裡不提純粹的表驅動法,因為對於項數未知的Fibonacci數列開啟大片的空間來換取時間未免不值得且不負責。我們只是為了消除遞迴法中大量重複的運算,可以將已經計算過的中間值存入一個表,已備後續使用:

#define MAX_LOG 20            static unsigned long Logs[MAX_LOG] = {0};            unsigned long Fib(int n)            {            if (n <= 1) {            return n;            } else if (n < MAX_LOG && Logs[n] != 0) {            return Logs[n];            } else {            Logs[n] = Fib(n - 1) + Fib(n - 2);            return Logs[n];            }            }

  當n小於儲存的表長時,由於每個中間值只計算一次,時間複雜度降為O(n)。但隨著n的增大,要想維持O(n)的時間複雜度,就必須擴大儲存的表長,這就造成了儲存空間的浪費。

  3、迭代法。求Fibonacci數列第n項時雖然要用到前面兩項的值,但它們僅作為臨時計算的中間值,不作為結果輸出,因此無保留的必要,完全可以轉化成迭代法求解:

unsigned long Fib(int n)            {            int i;            unsigned long a = 0, b = 1, c;            if (n <= 1) {            return n;            } else {            for (i = 2; i <= n; i++) {            c = a + b;            a = b;            b = c;            }            return c;            }            }

  迭代法的時間複雜度為O(n),使用的記憶體空間也不會動態上漲。個人認為Fibonacci數列更適宜作為迭代法而非遞迴法的典例出現在教材上。

  下面給出兩種數學性較強的演算法。考慮到表達的簡潔性,用Matlab實現:

  4、轉移矩陣法。此方法通常見於線性代數中的Markov過程樣本。Fibonacci數列第n項與第n-1項可以通過轉移矩陣的n-1次迭代求出:

  代碼如下:

function y = Fib(n)            first = [1; 0];            trans = [1 1; 1 0];            last = trans ^ (n - 1) * first;            y = last(1, 1);            end

  此演算法的時間複雜度相當於計算矩陣乘方的時間複雜度。在計算2階矩陣n次方時,如果直接按矩陣乘法定義式展開,不加特別最佳化,其時間複雜度為O(n)。

  5、通項公式法。Fibonacci數列的通項公式如下(證明略):

  利用通項公式可以得到Fibonacci數列的任何項:

function y = Fib(n)            sr5 = sqrt(5);            y = uint32((((1 + sr5) / 2) ^ n - ((1 - sr5) / 2) ^ n) / sr5);            end

  該方法的時間複雜度貌似為O(1),但我們還應該考慮乘方運算的時間消耗。不加特別最佳化時,用乘法實現n次乘方的時間複雜度為O(n)。考慮到浮點數計算效率和精度問題,此方法在電腦實現時不如轉移矩陣法。

  下面再給出兩種對電腦實現有特別意義,但同時有一定局限性的實現方法(只是實現方法,不能稱為新的演算法)。其中使用了一些C++編程技巧,對使用其它語言實現也有一定的參考價值:

  6、模板元編程法。通常我們在C++中使用模板,僅限於類模板與函數模板。事實上C++支援模板元編程,理論上可以在編譯時間執行任何計算(甚至包含選擇、迴圈、遞迴等結構)。代碼如下:

#define Fib(N) FibT<N>::Val            template<int n> struct FibT            {            enum            {            Val = FibT<n - 1>::Val + FibT<n - 2>::Val            };            };            template<> struct FibT<0>            {            enum            {            Val = 0            };            };            template<> struct FibT<1>            {            enum            {            Val = 1            };            };

  我們用一個結構體作為模板的載體,用一個枚舉值儲存運算結果。其中第一個模板為基本遞迴過程(使用遞迴演算法是為了說明的簡便,完全可以用其它演算法替代,以加速編譯過程),後兩個模板為n=0、1時的模板特化。通過#define語句將模板調用簡寫成類似函數調用的方式。程式在編譯時間運算所需的Fibonacci數列項,將結果作為常量嵌入編譯好的程式。運行時直接使用結果,時間複雜度真正地變成了O(1)。但這一方法最大的局限就是只能對常量嵌入,程式中出現諸如計算Fib(i++)的情況則無能為力。儘管如此,這比在代碼中手工計算並寫入所需的值要直觀準確,比通過純粹的表驅動法“空間換時間”要方便快捷。

  7、函數對象法。此方法主要用於C++ STL編程的通用演算法方面,其執行行為也有別於以上其它方法:

class Fib            {            public:            Fib() : a(0), b(1), n(0)            {            }            unsigned long operator()()            {            if (n <= 1) {            n++;            return n - 1;            } else {            int c;            c = a + b;            a = b;            b = c;            return c;            }            }            private:            int a, b, n;            };

  這個函數類對象的行為可以理解為一個“Fibonacci數列發生器”,其測試性調用如下,程式將依次列印

void test(int i)            {            Fib fib;            do {            cout << fib() << endl;            } while (i--);            }

  函數對象具有與函數指標類似的行為,同時又能儲存自身的一些屬性,因此常用於STL通用演算法編程。但針對單個的Fibonacci數列項求值,靈活性不如一般的方法。

  希望讀者能夠從上面的演算法分析中舉一反三,有所領悟。

參考資料:
  1、Bruce Eckel,Thinking In C++ Volume 2: Practical Programming,機械工業出版社,2006
  2、William J. Collins,Data Structures and the Standard Template Library,機械工業出版社,2003
  3、Knott's Surrey University,The Home page for Fibonacci Numbers and the Golden Section,http://www.mcs.surrey.ac.uk/Personal/R.Knott/Fibonacci/

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.