斐波拉契數列=>多種方法的比較(分治、遞迴、動態規劃/遞推)
阿新 • • 發佈:2019-01-21
斐波拉契數列是一個很不錯的例子,它的第一項和第二項都為1,以後的每一項都是前兩項的和。
這樣,斐波拉契數列可以有很多種解法。
首先用遞迴:
普通遞迴的方法存在很多的重複計算。效率自然很低。比如在算f(n-1)的時候,已經把f(n-2)算出來了,但是遞迴還要再算一次f(n-2)。//遞迴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; }
所以採用分治,一段一段計算,減少重複計算。
二分:
二分效率也是極其有限的,普通遞迴大概算到40左右已經是極限,而二分可以算到75左右。//二分分治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; }
所以進一步拆分問題,使他重複得更少。
三分:
三分已經可以在短時間內算出第110項左右了。接下來果斷六分://三分分治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; }
//六分分治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項毫不費力。這樣看來,這樣的方法速度和效率都是非常高的。
斐波拉契數列 不同得解法還很多,這裡不再列舉。