1. 程式人生 > >斐波那契數列Fibonacci實現(遞迴、尾遞迴、迴圈)

斐波那契數列Fibonacci實現(遞迴、尾遞迴、迴圈)

一、遞迴

簡單的來說遞迴就是一個函式直接或間接地呼叫自身,是為直接或間接遞迴。

遞迴一般用於解決三類問題:

  (1)資料的定義是按遞迴定義的。(Fibonacci函式,n的階乘)
  (2)問題解法按遞迴實現。(回溯)
  (3)資料的結構形式是按遞迴定義的。(二叉樹的遍歷,圖的搜尋)

用線性遞迴實現Fibonacci函式,程式如下所示:

1 int FibonacciRecursive(int n)
2 {
3     if( n < 2)
4         return n;
5     return (FibonacciRecursive(n-1)+FibonacciRecursive
(n-2)); 6 }

這裡寫圖片描述

遞迴的缺點:

  遞迴解題相對常用的演算法如普通迴圈等,執行效率較低。因此,應該儘量避免使用遞迴,除非沒有更好的演算法或者某種特定情況,遞迴更為適合的時候。

在遞迴呼叫的過程當中系統為每一層的返回點、區域性量等開闢了棧來儲存,因此遞迴次數過多容易造成棧溢位。

二、尾遞迴

尾遞迴就是從最後開始計算, 每遞迴一次就算出相應的結果, 也就是說, 函式調用出現在呼叫者函式的尾部, 因為是尾部, 所以根本沒有必要去儲存任何區域性變數. 直接讓被呼叫的函式返回時越過呼叫者, 返回到呼叫者的呼叫者去。精髓:尾遞迴就是把當前的運算結果(或路徑)放在引數裡傳給下層函式

,深層函式所面對的不是越來越簡單的問題,而是越來越複雜的問題,因為引數裡帶有前面若干步的運算路徑。

尾遞迴是極其重要的,不用尾遞迴,函式的堆疊耗用難以估量,需要儲存很多中間函式的堆疊。

採用尾遞迴實現Fibonacci函式,程式如下所示:

1 int FibonacciTailRecursive(int n,int ret1,int ret2)
2 {
3    if(n==0)
4       return ret1; 
5     return FibonacciTailRecursive(n-1,ret2,ret1+ret2);
6 }

這裡寫圖片描述

尾遞迴實現Fibonacci函式(C++實現)

#include <iostream>
#include <time.h>
using namespace std;
/**
    斐波那契數列Fibonacci
    一對小兔子兩月具有繁殖能力,每月生一對
    經過月數:0 1 2 3 4 5 6  7 ...
    兔子對數:0 1 1 2 3 5 8 13 ...
*/

//傳統遞迴實現normal recursion
unsigned long fib_normal_rec(unsigned long n)
{
    if (n <= 2)
        return 1;
    else
        return fib_normal_rec(n-1) + fib_normal_rec(n-2);
}

//尾遞迴實現Rail recusion
unsigned long fib_rail_rec(unsigned long n, unsigned long first, unsigned long second)
{
    if (n == 1) return first;
    if (n == 2) return second;
    return fib_rail_rec(n-1, second, second+first);
}
unsigned long fib_rail_rec(unsigned long n)
{
    return fib_rail_rec(n, 1, 1);
}

//迴圈實現
unsigned long fib_no_rec(unsigned long n)
{
    if (n <= 2)
        return 1;
    unsigned int x=1, y=1, y_tmp=0;
    //else if(n>=3)
    for (unsigned int i=0; i<n-2; i++)
    {
        y_tmp = y;
        y = x+y;
        x = y_tmp;
    }
    return y;
}

double calcRunTime(clock_t start, clock_t finish)
{
    return (double)(finish-start)/CLOCKS_PER_SEC;
}

int main()
{
    //第50個斐波那契數已經快接近(2^32)-1了,而且(win7 64位 i7-3.4GHz*8核)跑了54秒
    //再大時間太長沒等了,普通遞迴實現斐波那契數執行時間基本為指數增長,且因遞迴呼叫耗CPU,棧開銷耗記憶體
    //第幾個--斐波那契數是幾:24--46368; 25--75025
    unsigned long order_num = 50; //表示第order_num個斐波那契數
    unsigned long fib_num = 0;    //表示第order_num個斐波那契數是fib_num

    clock_t start0, finish0, start1, finish1, start2, finish2;

    start0 = clock();
    fib_num = fib_normal_rec(order_num);
    finish0 = clock();
    cout << "第" << order_num << "個斐波那契數是:" << fib_num <<endl;
    cout << "普通遞迴實現斐波那契數耗時(秒):" << calcRunTime(start0, finish0) << endl;
    cout << "----------------------------------------------------------" << endl;

    start1 = clock();
    fib_num = fib_rail_rec(order_num);
    finish1 = clock();
    cout << "第" << order_num << "個斐波那契數是:" << fib_num <<endl;
    cout << "尾遞迴實現斐波那契數耗時(秒):" << calcRunTime(start1, finish1) << endl;
    cout << "---------------------------------------------------------" << endl;

    start2 = clock();
    fib_num = fib_no_rec(order_num);
    finish2 = clock();
    cout << "第" << order_num << "個斐波那契數是:" << fib_num <<endl;
    cout << "普通迴圈實現斐波那契數耗時(秒):" << calcRunTime(start2, finish2) << endl;
    cout << "----------------------------------------------------------" << endl;

    system("pause");
    return 0;
}

這裡寫圖片描述