從斐波那契數列引入動態規劃

 斐波那契數列定義

        F(0)=1,F(1)=1,F(n)=F(n-1)+F(n-2)

       求F(n)

解法1:遞歸

#include <iostream>
using namespace std;

int fib(int n)
{
	if(n==0)
		return 0;
	if(n==1)
		return 1;
	return fib(n-1)+fib(n-2);
}

int main()
{
	cout<<fib(40)<<endl;
	return 0;
}

時間複雜度:O(2^n)  ,是一個指數級的時間複雜度

那麼現在我們來看下如何優化這個算法。

計算F(5)的過程如下:

可以看出,在這棵遞歸樹上,我們進行了多次重複的計算。比如fib(3)計算了2次,fib(2)計算了3次

那麼就會有一個想法,對於重複的計算,我們有沒有可能只計算一次呢。

如何避免重複運算??

在程序中設置一個全局變量數組

解法2:記憶化搜索

#include <iostream>
#include <vector>
using namespace std;
//在fib基礎上加上記憶化搜索
//遞歸中多次重複計算fib(i) 將計算過的值存儲下來,就可以避免重複計算 
vector<int> memo;  //存儲fib(i)的值 
int fib(int n)
{
	if(n==0)
		return 0;
	if(n==1)
		return 1;
	if(memo[n]==-1) //只有當fib(n)沒有被計算過時,才遞歸計算 
		 memo[n] = fib(n-1)+fib(n-2);
	return memo[n];
}

int main()
{
	int n=40;
	memo = vector<int>(n+1,-1);
	cout<<fib(n)<<endl;
	return 0;
}

通過這種方式,就能夠避免重複的計算,每個n只計算一次fib值,算法複雜度O(n)

所以記憶化搜索的實質是在遞歸的基礎上加上記憶化,是一種自上而下解決問題的方式

解法3:動態規劃

動態規劃是一種自下向上的解決問題的方式

動態規劃的定義:

將原問題拆解成若干子問題,同時保存子問題的答案,使得每個子問題只求解一次,最終獲得原問題的答案

大多數動態規劃問題,其本質其實就是遞歸問題

先解決小數據的問題,然後層層遞推,解決更大數據量的問題

#include <iostream>
#include <vector>
using namespace std;
//使用動態規劃解fib 
int fib(int n)
{
	vector<int> memo;  //存儲fib(i)的值 
	memo = vector<int>(n+1,-1);
	memo[0]=0;
	memo[1]=1;
	
	//求解每一個fib(i) 
	for(int i=2; i<=n; i++)
		memo[i] = memo[i-1] + memo[i-2];
	return memo[n];
}

int main()
{
	int n=40;
	cout<<fib(n)<<endl;
	return 0;
}

時間複雜度O(n)

總結:三者的關係

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章