一:斐波那契數列問題的起源java
13世紀初期,意大利數論家Leonardo Fibonacci在他的著做Liber Abaci中提出了兔子的繁殖問題:算法
若是一開始有一對剛出生的兔子,兔子的長大須要一個月,長大後的兔子每月能生產一對兔子,假設兔子不會死亡,那麼一年後有多少隻兔子?數組
不難看出每月的兔子的總數能夠用如下數列表示:1,1,2,3,5,8,13......this
二:最直觀的算法spa
1.算法實現3d
經過觀察咱們不難發現斐波那契數列從第三項開始每一項都是前兩項的和,所以咱們不難總結出該數列的遞推公式:code
根據此地推公式咱們能夠很直觀地得出斐波那契數列地二分遞歸實現:blog
1 long Fib(int n){ 2 return (2 > n) ? (long) n : Fib(n -1) + Fib(n - 2); 3 }
2.時間複雜度分析 遞歸
雖然此種算法實現簡單一目瞭然但該算法地效率極其低下,下面分析一下該算法地時間複雜度:ci
按照該算法地思路,將Fib(n)所需地時間記爲T(n)則T(n) = T(n - 1) + T(n - 2)。由此咱們能夠得出該時間複雜度地遞推公式
爲了便於計算咱們將n 大於等於 2 地公式寫做
不難看出這是一個二階常係數齊次差分方程
假設爲方程的一個解,則
特徵方程的兩個根爲:
因此通解爲: 其中C1 ,C2爲常數,將 帶入方程可得:
能夠看出此種遞歸算法的時間複雜度爲指數量級,所以隨着n的增大
算法所需的時間也會急劇上升。
三:線性時間複雜度版本
1.算法實現
上一種算法之因此會產生指數量級的時間複雜度,是由於算法是根據表面定義Fib(n) = Fib(n - 1) +Fib(n - 2)的誤導,事實上子問題
Fib(n - 1)和Fib(n - 2)並非獨立的。
線性迭代算法:
public class Test { public static void main(String[] args) { System.out.println(Fib(8)); } //線性複雜度迭代,常數空間複雜度 public static int Fib(int n) {//計算第n項 int prev = 0; int next = 1;//初始化:Fib(0) = 0,Fib(1) = 1 while(n-- > 1) { next = prev + next; prev = next - prev;//經過n次加減計算Fib(n) } return next; } }
以上算法不只只需O(n)時間,並且只需常數規模的附加空間
經過以上迭代算法,咱們能夠實現以下線性遞歸版本:
1 public class Test { 2 3 public static void main(String[] args) { 4 System.out.println(Fib(8,0,1)); 5 } 6 /* n:求斐波那契數列的第n項 7 * first:Fib(0) = 0 8 * second: Fib(1) = 1 9 * */ 10 public static int Fib(int n,int first,int second) { 11 if(n < 2) { 12 return n; 13 } 14 if(n == 2) { 15 return first + second;//遞歸基 16 }else{ 17 return Fib(n - 1,second,first+second); 18 } 19 } 20 }
以上兩種方法都是利用變量記錄相鄰兩項的值,而後相加算出下一項的值,從而實現線性時間複雜度。
四,利用矩陣相乘實現O(logn)複雜度的算法
根據斐波那契數列的遞推公式,可使用矩陣相乘的形式:
因此經過n - 1次矩陣乘法就能算出第n項和第n-1項的值,其中前n-2次矩陣乘法乘數都是同一個矩陣,咱們
將子問題:求 分解爲求該矩陣(n-1)/2次方的平方,再分解爲(n - 1)/4次方的平方的平方...
如: 只需計算3次便可,因此時間複雜度爲對數量級。
爲實現該算法,須要一個矩陣類,能處理矩陣之間的乘法,並能獲取指定元素,完整代碼以下:
1 import java.util.ArrayList; 2 import java.util.Scanner; 3 public class MatrixTest{ 4 public static void main(String[] args) { 5 Scanner sc = new Scanner(System.in); 6 int num = sc.nextInt(); 7 System.out.println(matrixFib(num)); 8 } 9 10 public static long matrixFib(int num) { 11 if(num <= 1){ 12 return num; 13 } 14 Matrix first = new Matrix(2, 2); 15 first.setElement(1, 1, 1); 16 first.setElement(1, 2, 1); 17 first.setElement(2, 1, 1); 18 first.setElement(2, 2, 0); 19 20 Matrix result = new Matrix(2,1); 21 result.setElement(1, 1, 1); 22 result.setElement(2, 1, 0);//注意矩陣乘法的順序是固定的,兩個矩陣不能交換 23 //輸入num表明第幾項斐波那契數 24 int n = num - 1;//根據遞推式求第num項,只需求first矩陣的num - 1次方 25 while(n > 0) { 26 if(n % 2 != 0) { 27 result = first.MultiMatri(result); 28 } 29 if((n /= 2) > 0)//當n/= 2 < 0 時,下次不會進入循環,因此不必再運算一次 30 first = first.MultiMatri(first); 31 //n /= 2;//當輸入規模足夠大時,每次都判斷的代價大於多作一次矩陣乘法 32 } 33 return result.getElement(1, 1); 34 } 35 } 36 class Matrix { 37 //成員變量:一個看成矩陣的二維數組 38 private int row;//當前矩陣的行數 39 private int col;//當前矩陣的列數 40 public ArrayList<ArrayList<Integer>> matrix;//二維數組用於保存矩陣 41 //傳入行數和列數構造一個零矩陣 42 public Matrix(int row, int col) { 43 this.row = row; 44 this.col = col; 45 matrix = new ArrayList<ArrayList<Integer>>(row); 46 for(int i = 0;i < row;i++) { 47 ArrayList<Integer> list = new ArrayList<Integer>(col); 48 for(int j = 0;j < col;j++) { 49 list.add(0); 50 } 51 matrix.add(list); 52 } 53 } 54 public int getRow() {//獲取矩陣行數 55 return row; 56 } 57 58 public int getCol() {//獲取矩陣列數 59 return col; 60 } 61 62 public ArrayList<ArrayList<Integer>> getMatrix() //返回保存矩陣的二維數組 63 { 64 return matrix; 65 } 66 67 //獲取元素a[row][col] 68 public int getElement(int row,int col) { 69 return matrix.get(row - 1).get(col - 1); 70 } 71 //設置a[row][col] = value 72 public void setElement(int row,int col,int value) { 73 matrix.get(row - 1).set(col - 1, value); 74 } 75 //獲取某一行向量的值 76 public ArrayList<Integer> getRow(int row){ 77 return matrix.get(row - 1); 78 } 79 //獲取某一列向量的值 80 public ArrayList<Integer> getCol(int col){ 81 ArrayList<Integer> arrCol = new ArrayList<Integer>(); 82 for(int i = 0;i < row;i++) { 83 arrCol.add(matrix.get(i).get(col - 1)); 84 } 85 return arrCol; 86 } 87 //向量點乘 88 public int MultiVec(ArrayList<Integer> v1,ArrayList<Integer> v2) 89 { 90 if(v1.size() != v2.size()) { 91 return -1; 92 } 93 int result = 0; 94 for(int i = 0;i < v1.size();i++) { 95 result += (v1.get(i))* (v2.get(i)); 96 } 97 return result; 98 } 99 //矩陣乘法,只有第一個矩陣的列數等於第二個矩陣的行數才能相乘 100 public Matrix MultiMatri(Matrix matri1) { 101 if(getCol() != matri1.getRow()) 102 return null; 103 Matrix matri2 = new Matrix(getRow(),matri1.getCol());//新矩陣的行列 104 for(int i = 1;i <= getRow();i++) { 105 for(int j = 1;j <= matri1.getCol();j++) { 106 matri2.setElement(i, j, MultiVec(getRow(i),matri1.getCol(j))); 107 } 108 } 109 return matri2; 110 } 111 }
以上就是筆者總結的關於斐波那契數列的各類算法實現。