裴波那契数列的递归和动态规划算法
裴波那契數(shù)列的遞歸和動態(tài)規(guī)劃算法
一、??? 概論
通過對裴波那契數(shù)列的例子,分析了遞歸和動態(tài)規(guī)劃算法的本質(zhì)。并且說明了兩種算法的區(qū)別。
裴波那契數(shù)列:800年前,意大利的數(shù)學(xué)家斐波納契出版了驚世之作《算盤書》。在《算盤書》里,他提出了著名的“兔子問題”:假定一對兔子每個月可以生一對兔子,而這對新兔子在出生后第二個月就開始生另外一對兔子,這些兔子不會死去,那么一對兔子一年內(nèi)能繁殖多少對兔子?
答案是一組非常特殊的數(shù)字:1,1,2,3,5,8,13,21,34,55,89……不難發(fā)現(xiàn),從第三個數(shù)起,每個數(shù)都是前兩數(shù)之和,這個數(shù)列則稱為“斐波納契數(shù)列”,其中每個數(shù)字都是“斐波納契數(shù)”。
斐波納契數(shù)列還暗含著許多有趣的數(shù)字規(guī)律,如從第3個數(shù)開始每隔兩個必是2的倍數(shù),從第4個數(shù)開始每隔3個必是3的倍數(shù),從第5個數(shù)開始每隔4個必是5的倍數(shù)……另外,這個數(shù)列最具有和諧之美的地方是,越往后,相鄰兩項的比值會無限趨向于黃金比0.61803……即[5^(1/2)-1]/2。
但這個偉大的發(fā)現(xiàn)在當(dāng)時一直不受數(shù)學(xué)們的青睞與認(rèn)可,直到19世紀(jì),斐波納契數(shù)列才在該領(lǐng)域占有一席之地并引發(fā)出了許多重要的應(yīng)用。像斐波納契方塊,斐波納契螺旋以及斐波納契數(shù),在生活中都可以見到類似的圖案,譬如說海螺和蝸牛殼等等。
----------------------------以上來自360百科
這個數(shù)列感覺還是非常有意思的。感興趣的同學(xué)可以多多研究一下。另外本文只是本人一個簡單的分享感悟,有很多不正確的地方,請大家指正。
二、??? 裴波那契數(shù)列的遞歸分析
由上面的介紹可以得出裴波那契數(shù)列的遞歸算式:
也很容易寫出下面的遞歸算法:
#include <iostream>
using namespace std;
//采用遞歸的方式求解裴波納契數(shù)列
int f(int n)
{
if(n==0)return 1;
if(n==1)return 1;
return f(n-1)+f(n-2);
}
int main()
{
//輸出數(shù)列的前20項
for(int i=0;i<20;i++)
{
cout<<"第i項:"<<f(i)<<endl;
}
return 0;
}
輸出結(jié)果如圖:
遞歸算法時間復(fù)雜度分析:
由上面的遞歸樹可以分析,最右邊是每次減少2,因此右邊的層次數(shù)是最低的,因此可以通過右邊的層次來計算最少的時間復(fù)雜度。
每次個節(jié)點都產(chǎn)生兩個子節(jié)點,因此子節(jié)點的個數(shù),就是要計算的復(fù)雜度,即總共需要計算:
c是一個常數(shù),即兩個數(shù)相加需要的時間。
根據(jù)上述等比數(shù)列,可以知道時間復(fù)雜度為指數(shù)級別(具體多少讀者可以根據(jù)上面的算式計算)。
因此采用遞歸會導(dǎo)致大量的重復(fù)計算,例如在圖中的f(n-3)就會被計算兩次。同時,由于計算機本身的性質(zhì),遞歸次數(shù)不能過多,否則會導(dǎo)致棧溢出,導(dǎo)致程序崩潰。因此這里采用遞歸算法不僅造成時間復(fù)雜度很大,同時還造成棧溢出的可能。
本例中,當(dāng)采用遞歸求解第40項的時,速度已經(jīng)很慢,求解第50項的時,已經(jīng)沒有響應(yīng)了。
三、??? 裴波那契數(shù)列的動態(tài)規(guī)劃分析
動態(tài)規(guī)劃的主要兩個步驟:
a)???????? 最優(yōu)解的結(jié)構(gòu)
f(i)=f(i-1)+f(i-2),這就是最優(yōu)解的結(jié)構(gòu),只不過不像一般的問題,這里的子結(jié)構(gòu)都是固定的
b)???????? 遞歸定義最優(yōu)解
這里的遞歸定義和遞歸算法中的公式是一樣的。
c)???????? 自底向上
從底向上的求解,這里是最大的區(qū)別。遞歸算法是從上到下的計算,即將大問題分別分解成若干個小問題,然后遞歸求解小問題,直到達(dá)到原子問題,然后開始逐步返回,計算每個大的問題。即存在某些問題會反復(fù)計算多次。
而從底向上的解法,則是從小問題開始,計算完成后就記錄下小問題的解,然后通過小問題的解,構(gòu)造出大問題的解,如此重復(fù)。即每個問題只需要計算一次即可。
d)???????? 由結(jié)果構(gòu)造最優(yōu)解
動態(tài)規(guī)劃算法:
#include <iostream>
using namespace std;
//采用遞歸的方式求解裴波納契數(shù)列
int f(int n)
{
if(n==0)return 1;
if(n==1)return 1;
return f(n-1)+f(n-2);
}
//采用動態(tài)規(guī)劃思想求解裴波納契數(shù)列
static int record[40]={0};
void count(int n)
{
record[0]=1;
record[1]=1;
for(int i=2;i<n;i++)
{
record[i]=record[i-1]+record[i-2];
}
}
int main()
{
//輸出前20項
count(20);
for(int i=0;i<20;i++)
{
cout<<record[i]<<endl;
}
return 0;
}
輸出結(jié)果:
時間復(fù)雜度分析:
比較容易知道,上面的算法只是到n的一個簡單循環(huán),因此時間復(fù)雜度是o(n),即n的線性時間復(fù)雜度。
本例中,當(dāng)采用遞歸求解第40和第50項項的時,速度很快。
四、??? 結(jié)合了動態(tài)規(guī)劃和遞歸兩種方法的思想求解裴波納契數(shù)列
結(jié)合兩種方法的優(yōu)點,即采用遞歸容易理解的結(jié)構(gòu),同時加入動態(tài)規(guī)劃中的記錄表,防止遞歸中的重復(fù)計算。即記錄遞歸過程中產(chǎn)生的每一個值,如果遇到已經(jīng)曾經(jīng)計算過的小問題的值,則不再重復(fù)遞歸,直接返回即可。
算法如下:
#include <iostream>
using namespace std;
//采用遞歸的方式求解裴波納契數(shù)列
int f(int n)
{
if(n==0)return 1;
if(n==1)return 1;
return f(n-1)+f(n-2);
}
//采用動態(tài)規(guī)劃思想求解裴波納契數(shù)列
static int record[40]={0};
void count(int n)
{
record[0]=1;
record[1]=1;
for(int i=2;i<n;i++)
{
record[i]=record[i-1]+record[i-2];
}
}
//結(jié)合了動態(tài)規(guī)劃和遞歸兩種方法的思想求解裴波納契數(shù)列
static int c[40]={1,1,0};
int f_count(int n)
{
if(n==0)return c[0];
if(n==1)return c[1];
//如果兩個子問題已經(jīng)存在解 則直接返回這個解,并且記錄當(dāng)前問題的解
if(c[n-1]!=0 && c[n-2]!=0)
{
c[n]=c[n-1]+c[n-2];
return c[n];
}
//如果不存在,則遞歸求解
else
{
return f_count(n-1)+f_count(n-2);
}
}
int main()
{
//輸出前20項
f_count(20);
for(int i=0;i<20;i++)
{
cout<<c[i]<<endl;
}
return 0;
}
算法輸出:
時間復(fù)雜度分析:
由于記錄了曾經(jīng)計算過的值,因此導(dǎo)致很多點不再被重復(fù)計算,僅僅是一個簡單的一重遞歸,即時間復(fù)雜度為o(n),即為n的線性時間。
本例中,當(dāng)采用遞歸求解第40和第50項項的時,速度同樣很快。
五、??? 總結(jié)
個人感覺,其實在某種程度上來說,動態(tài)規(guī)劃和分治法是有著相同的思想。分治法的思想是將大問題分解為小問題。而動態(tài)規(guī)劃思想是取最優(yōu)子結(jié)構(gòu)。但是從計算方法上來說,分治法是自頂向下的分解問題,然后自底向上返回問題的解。而動態(tài)規(guī)劃一開始就直接從底向上的將小問題的解綜合成大問題的解。動態(tài)規(guī)劃相對分治法中的遞歸算法時間上是采用了以空間換時間的方式。即在計算小問題的過程中記錄下小問題的值。而遞歸是不會記錄小問題的值,每次都是重復(fù)計算。
而采用兩種方法相結(jié)合的方式也同樣可以提高運行速度。其內(nèi)在原因就是是否重復(fù)計算的問題。
文檔下載地址:http://pan.baidu.com/share/link?shareid=531539743&uk=2903070410
源代碼下載地址:http://pan.baidu.com/share/link?shareid=535428463&uk=2903070410
文章來自IT部落格(www.itbuluoge.com)
總結(jié)
以上是生活随笔為你收集整理的裴波那契数列的递归和动态规划算法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 充电桩通过WiFi付费和管理方案
- 下一篇: 名帖204 蔡襄 行书《行书帖选》