PHP實現斐波那契數列

在數學上,斐波納契數列以如下被以遞歸的方法定義:
F(0)=0,F(1)=1,F(n)=F(n-1)+F(n-2)(n≥2,n∈N*)

使用遞歸實現

很直觀表示方式,用遞歸很容易實現:

function f($a)
{
    if ($a == 0 || $a == 1) {
        return 1;
    }
    return f($a-1) + f($a-2);
}

for($i = 0; $i < 70; ++$i)
{
    echo f($i).' ';
}

但是運行的時候,發現上面代碼運行會超時:

這裏寫圖片描述

遞歸其實是方便了程序員難爲了機器。它只要得到數學公式就能很方便的寫出程序。優點就是易理解,容易編程。但遞歸是用棧機制實現的,每深入一層,都要佔去一塊棧數據區域,對嵌套層數深的一些算法,遞歸會力不從心,空間上會以內存崩潰或超時而告終,而且遞歸也帶來了大量的函數調用,這也有許多額外的時間開銷。所以在深度大時,它的時空性就不好了。

那麼如果非要用遞歸,有什麼辦法可以改進呢?

可以看到,在打印的過程中,比如求 f(6) 調用 f(5) 和 f(4)時,之前的某次循環其實已經算出來了 f(5) 和 f(4) ,不需要再算了。

可以使用一個數組,來保存之前的結果

function f($n, $arr)
{
    global $arr;
    if( isset($arr[$n]) ) {  //比 array_key_exits()效率高
        return $arr[$n];
    }
    return f($n-1, $arr) + f($n-2, $arr);
}
$arr = [
    0 => 1,
    1 => 1,
];
for($i = 0; $i < 1000000; ++$i)
{
    $arr[$i] = f($i, $arr);
    echo f($i, $arr).' ';
}

使用PHP7,打印1000000個也沒有問題。

使用循環實現

<?php 
$arr[1] = 1;
for($i = 2;$i < 100;$i++)
{
    $arr[$i] = $arr[$i-1] + $arr[$i-2];
}
echo join(",",$arr);  //將數組合併爲一個字符串輸出
?>

非遞歸實現比遞歸效率高很多。

遞歸和循環的簡單比較:

1、從程序上看,遞歸表現爲自己調用自己,循環則沒有這樣的形式。
2、遞歸是從問題的最終目標出發,逐漸將複雜問題化爲簡單問題,並且簡單的問題的解決思路和複雜問題一樣,同時存在基準情況,就能最終求得問題,是逆向的。而循環是從簡單問題出發,一步步的向前發展,最終求得問題,是正向的。
3、任意循環都是可以用遞歸來表示的,但是想用循環來實現遞歸(除了單向遞歸和尾遞歸),都必須引入棧結構進行壓棧出棧。
4、一般來說,非遞歸的效率高於遞歸。而且遞歸函數調用是有開銷的,遞歸的次數受堆棧大小的限制。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章