Fibonacci數列遞歸的實現
- 先來一個fibonacci數列的定義:
Fibonacci數列指的是這樣一個數列:1、1、2、3、5、8、13、21、34、……在數學上,斐波那契數列以如下被以遞推的方法定義:F(1)=1,F(2)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 3,n ∈ N* 。
Fibonacci數列在程序中的實現還是很容易,他是一個典型的可以用遞歸現實的算法。 - 我們先來一個普通的遞歸寫法:
int fibo(int n)
{
if(n == 1 || n == 2)
return 1;
return fibo(n-1)+fibo(n-2);
}
int main()
{
int n,result;
printf("請輸入:");
scanf("%d",&n);
result = fibo(n);
printf("%d\n",result);
}
遞歸代碼簡潔,但是如果不做一定的優化,很容易出現棧溢出。以上的實現就會非常耗費內存,因為當n>2時,fibo函數需要調用自身n-2次才開始有返回值,然后開逐個返回原函數並開始計算。如果要求的n值非常大的話,可能需要同時保存成千上百個調用記錄,很容易發生"棧溢出"錯誤(stack overflow)。
- 但是我們可以對以上實現做一個小優化-尾遞歸
尾遞歸顧名思義-在尾部遞歸,也就是在函數執行的最后一步調用函數自身,並且,最后一步不能包含表達式。這樣,編譯器或者解釋器就可以把尾遞歸做優化,使遞歸本身無論調用多少次,都只占用一個棧幀,不會出現棧溢出的情況。
一般來說,由於只存在一個調用記錄,所以永遠不會發生"棧溢出"錯誤。
return是用來結束執行函數、並返回結果的語句,常用來做尾遞歸使用。例如:上述的 return fibo(n-1)+fibo(n-2);
有+
運算符,最后一步包含了表達式;如果把+
號及后面函數調用去掉,只保留 return fibo(n-1);
則為尾遞歸,但這樣達不到想要的結果,所以下面就需要一些小改動。
優化后的遞歸函數:
int fibo(int n,int i,int j)
{
if(n ==1 || n ==2)
return j;
return fibo(n-1,j,j+i);
}
int main()
{
int n,result;
printf("請輸入:");
scanf("%d",&n);
result = fibo(n,1,1);
printf("%d\n",result);
}
這樣一來,當fibo函數調用自身,就開始有return值,在遞歸結束后不用再把return值逐個返回原函數。尾遞歸的實現方式,編譯器可以幫我們節省大量的內存消耗,媽媽再也不用我的棧溢出了!