1. 程式人生 > >斐波拉契數列=>多種方法的比較(分治、遞迴、動態規劃/遞推)

斐波拉契數列=>多種方法的比較(分治、遞迴、動態規劃/遞推)

斐波拉契數列是一個很不錯的例子,它的第一項和第二項都為1,以後的每一項都是前兩項的和。

這樣,斐波拉契數列可以有很多種解法。

首先用遞迴:

//遞迴for斐波那契數列 
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
using namespace std;
long long work(int n){
if(n==1||n==2){
	return 1;
}
else{
	return work(n-1)+work(n-2);
}
}
int main()
{int n;
//freopen("fei.in","r",stdin);
//freopen("fei.out","w",stdout);
scanf("%d",&n);
printf("%lld",work(n));
return 0;	
}
普通遞迴的方法存在很多的重複計算。效率自然很低。比如在算f(n-1)的時候,已經把f(n-2)算出來了,但是遞迴還要再算一次f(n-2)。

所以採用分治,一段一段計算,減少重複計算。

二分:

//二分分治for斐波那契 
#include<cstdio>
double n;
int k;
long long s,a;
long long sishewuru(double g){
	if(g>=(int)g+0.5){
		return g+1;
	}
	else return g;
}
long long er(long long q){
if(q==1||q==2){
	return 1;
}
else{
	return er(q-1)+er(q-2);
}
}
long long wang(long long p){
	if(p==sishewuru(n/2)){
		return s;
	}
	if(p==sishewuru(n/2)-1){
		return a;
	}
	else return wang(p-1)+wang(p-2);
}
int main()
{//freopen("fei.in","r",stdin);
//freopen("fei.out","w",stdout);
scanf("%lf",&n);
s=er(sishewuru(n/2));
a=er(sishewuru(n/2)-1);
long long o=wang(n);
printf("%lld",o);
return 0;
}
二分效率也是極其有限的,普通遞迴大概算到40左右已經是極限,而二分可以算到75左右。

所以進一步拆分問題,使他重複得更少。

三分:

//三分分治for斐波那契數列 
#include<cstdio>
double n;
int k;
long long s,a;
long long f,d;
long long sishewuru(double g){
	if(g>=(int)g+0.5){
		return g+1;
	}
	else return g;
}
long long er(long long q){
if(q==1||q==2){
	return 1;
}
else{
	return er(q-1)+er(q-2);
}
}
long long wang(long long p){
	if(p==sishewuru(n/3)){
		return s;
	}
	if(p==sishewuru(n/3)-1){
		return a;
	}
	else return wang(p-1)+wang(p-2);
}
long long ring(long long i){
	if(i==sishewuru(2*n/3)){
		return f;
	}
	if(i==sishewuru(2*n/3-1)){
		return d;
	}
	else return ring(i-1)+ring(i-2);
}
int main()
{//freopen("fei.in","r",stdin);
//freopen("fei.out","w",stdout);
scanf("%lf",&n);
s=er(sishewuru(n/3));
a=er(sishewuru(n/3)-1);
f=wang(sishewuru(2*n/3));
d=wang(sishewuru(2*n/3)-1);
long long o=ring(n);
printf("%lld",o);
return 0;
}
三分已經可以在短時間內算出第110項左右了。接下來果斷六分:
//六分分治for斐波那契數列 
#include<cstdio>
double n;
int k;
long long s,a;
long long f,d;
long long w,q,e,r,t,y;
long long sishewuru(double g){
	if(g>=(int)g+0.5){
		return g+1;
	}
	else return g;
}
long long er(long long q){
if(q==1||q==2){
	return 1;
}
else{
	return er(q-1)+er(q-2);
}
}
long long wang(long long p){
	if(p==sishewuru(n/6)){
		return s;
	}
	if(p==sishewuru(n/6)-1){
		return a;
	}
	else return wang(p-1)+wang(p-2);
}
long long dao(long long x){
	if(x==sishewuru(n/3)){
		return f;
	}
	if(x==sishewuru(n/3-1)){
		return d;
	}
	else return dao(x-1)+dao(x-2);
}
long long yu(int v){
	if(v==sishewuru(n/2)){
		return w;
	}
	if(v==sishewuru(n/2-1)){
		return q;
	}
	else return yu(v-1)+yu(v-2);
}
long long nb(int m){
	if(m==sishewuru(2*n/3)){
		return r;
	}
	if(m==sishewuru(2*n/3-1)){
		return e;
	}
	else return nb(m-1)+nb(m-2);
}
long long ring(long long i){
	if(i==sishewuru(5*n/6)){
		return y;
	}
	if(i==sishewuru(5*n/6-1)){
		return t;
	}
	else return ring(i-1)+ring(i-2);
}
int main()
{//freopen("fei.in","r",stdin);
//freopen("fei.out","w",stdout);
scanf("%lf",&n);
s=er(sishewuru(n/6));
a=er(sishewuru(n/6)-1);
f=wang(sishewuru(n/3));
d=wang(sishewuru(n/3)-1);
w=dao(sishewuru(n/2));
q=dao(sishewuru(n/2-1));
r=yu(sishewuru(2*n/3));
e=yu(sishewuru(2*n/3-1));
y=nb(sishewuru(5*n/6));
t=nb(sishewuru(5*n/6-1));
long long o=ring(n);
printf("%lld",o);
return 0;
}
六分經測,可以短時間內算出200項左右,也許可以到210項。

由此發現,普通的遞迴可以到40左右,二分到75左右,三分110左右,六分200左右,可以看出,可以在短時間內算到的項數(n)與它分成多少段(r)成正比。

大概以40左右為係數。即:n=k*r.

但是這樣的方法也是極其有限的,想要計算到後面,就要不斷地擴大分段,很是麻煩。

所以可以直接利用遞推,用動態規劃算出來,效率會大大增強。

//遞推/動態規劃for斐波那契 
#include<cstdio>
long long a[10000];
int main()
{int n;
//freopen("fei.in","r",stdin);
//freopen("fei.out","w",stdout);
scanf("%d",&n);
a[1]=a[2]=1;
for(int i=3;i<=n;i++){
	a[i]=a[i-1]+a[i-2];
}
	printf("%lld",a[n]);
	return 0;
}
我定義了10000的整型陣列,可以發現,這種方法計算10000項毫不費力。這樣看來,這樣的方法速度和效率都是非常高的。

斐波拉契數列 不同得解法還很多,這裡不再列舉。