一:斐波那契数列问题的起源
13世纪初期,意大利数论家Leonardo Fibonacci在他的著作Liber Abaci中提出了兔子的繁殖问题:
如果一开始有一对刚出生的兔子,兔子的长大需要一个月,长大后的兔子每个月能生产一对兔子,假设兔子不会死亡,那么一年后有多少只兔子?
不难看出每个月的兔子的总数可以用以下数列表示:1,1,2,3,5,8,13......
二:最直观的算法
1.算法实现
通过观察我们不难发现斐波那契数列从第三项开始每一项都是前两项的和,因此我们不难总结出该数列的递推公式:
根据此地推公式我们可以很直观地得出斐波那契数列地二分递归实现:
1 long Fib(int n){ 2 return (2 > n) ? (long) n : Fib(n -1) + Fib(n - 2); 3 }
2.时间复杂度分析
虽然此种算法实现简单一目了然但该算法地效率极其低下,下面分析一下该算法地时间复杂度:
按照该算法地思路,将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 }
以上就是笔者总结的关于斐波那契数列的各种算法实现。
原文地址:https://www.cnblogs.com/acxilon/p/10264874.html