問題描述:斐波那契數列是這樣的一個數列,1,1,2,3,5,8,..,即前兩項都是1,後面每一項都是其前面兩項的和。
現在要你求出該數列的第n項。
分析:該問題是一個經典的數列問題,相信大家在很多語言的教科書上都碰到過這個練習題目。這裏我給大家總結了三種經典解法,並對這三個方法進行了對比。
解法一:遞歸算法。很多教科書上都用這個題作爲函數遞歸知識點講解的例題,我們可以將每一個項的求法表達爲這樣一個式子:
f(n)=f(n-1)+f(n-2),f(1)=1,f(2)=1,可以看出,可以採用遞歸算法求解。
解法二:循環求法。我們可以從第1項開始,一直求到第n項,即可,一個循環可以做到,時間複雜度爲O(n).
解法三:矩陣鏈乘法。如果線性代數學的好的話,可以想出這樣一種解法,
同樣可以採用遞歸的算法求解,時間複雜度爲O(logn).
三種解法對比:解法一編程最簡單,但是效率最低,因爲這種遞歸算法求解時,會重複求解子問題。如下圖示:
這樣就看出來吧!另外如果n很大的話,遞歸的層數很大,會消耗系統大量的時間和資源。
解法二避免了重複求解子問題,線性時間即可求出,值得采用。
解法三效率最高,但是編程特別複雜,在有些情況下,很合適使用,但就本題目來說,推薦解法二。
針對上述三種解法,我給出了詳細的Java代碼,讀者可以參考:
import java.util.*;
public class Main {
public static int f1(int n){ //方法一:遞歸算法,自底向上
if(n<=2)return 1; //如果是求前兩項,直接返回就可
else return f1(n-1)+f1(n-2);
}
public static int f2(int n){ //方法二:循環算法,自上而下
if(n<=2)return 1; //如果是求前兩項,直接返回就可
int a1=1,a2=1,a3;
for(int i=3;i<=n;i++)
{
a3=a1+a2;
a1=a2;
a2=a3;
}
return a2;
}
public static int[][] f3(int n){ //方法三:矩陣鏈相乘算法,採用遞歸實現
int a[][]={{1,1},{1,0}}; //定義基矩陣
int b[][]; //存儲子方法的結果
int c[][]=new int[2][2]; //存儲最後計算結果
int d[][]=new int[2][2]; //存儲中間計算結果
if((n)<=1)return a; //如果次方小等於1直接返回
else if((n) %2==1)
{b=f3((n-1)/2);
d[0][0]=b[0][0]*b[0][0]+b[0][1]*b[1][0];
d[0][1]=b[0][0]*b[0][1]+b[0][1]*b[1][1];
d[1][0]=b[1][0]*b[0][0]+b[1][1]*b[1][0];
d[1][1]=b[1][0]*b[0][1]+b[1][1]*b[1][1];
c[0][0]=d[0][0]*a[0][0]+d[0][1]*a[1][0];
c[0][1]=d[0][0]*a[0][1]+d[0][1]*a[1][1];
c[1][0]=d[1][0]*a[0][0]+d[1][1]*a[1][0];
c[1][1]=d[1][0]*a[0][1]+d[1][1]*a[1][1];
}
else {
b=f3((n)/2);
c[0][0]=b[0][0]*b[0][0]+b[0][1]*b[1][0];
c[0][1]=b[0][0]*b[0][1]+b[0][1]*b[1][1];
c[1][0]=b[1][0]*b[0][0]+b[1][1]*b[1][0];
c[1][1]=b[1][0]*b[0][1]+b[1][1]*b[1][1];
}
return c;
}
public static void main(String[] args) {
// TODO 自動生成的方法存根
Scanner scan=new Scanner(System.in);
int n=scan.nextInt();
System.out.println("方法一:"+f1(n));
System.out.println("方法二:"+f2(n));
int a[][]=f3(n-1); //因爲是要求矩陣{{1,0},{1,0}}的n-1次方
System.out.println("方法三:"+a[0][0]);
}
}
輸出結果爲:
10
方法一:55
方法二:55
方法三:55