几种复杂度的斐波那契数列的Java实现

一:斐波那契数列问题的起源

  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

时间: 2024-11-08 23:39:52

几种复杂度的斐波那契数列的Java实现的相关文章

用程序猿思维、程序设计师思维两种方式写求斐波那契数列的方法。

//用Java实现斐波那契数列(Fibonacci) public class Test { public int f(int n)//n代表第几个数字.程序返回它相应的值 { return n>2?f(n-1)+f(n-2):1;//看似如此优雅的一句程序 } //程序设计师的思维:会重构上面的代码.让他们更易读.推荐!! ! public int fibo(final int pos) { final int num; if(pos>2) num = fibo(pos-1)+fibo(po

用程序员思维、程序设计师思维两种方式写求斐波那契数列的方法。

//用Java实现斐波那契数列(Fibonacci) public class Test { public int f(int n)//n代表第几个数字,程序返回它对应的值 { return n>2?f(n-1)+f(n-2):1;//看似如此优雅的一句程序 } //程序设计师的思维:会重构上面的代码,让他们更易读,推荐!!! public int fibo(final int pos) { final int num; if(pos>2) num = fibo(pos-1)+fibo(pos

斐波那契数列【java实现】

java 实现斐波那契数列 以下是Java代码实现(递归与递推两种方式): import java.util.Scanner; /** * Fibonacci * * @author tongqian.zhang */ public class Fibonacci { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.println("Please input

算法小节(一)——斐波那契数列(java实现)

看到公司的笔试题中有一道题让写斐波那契数列,自己忙里偷闲写了一下 什么是斐波那契数列:斐波那契数列指的是这样一个数列 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368这个数列从第二项开始,每一项都等于前两项之和. 特别指出:第0项是0,第1项是第一个1. 注:此时a1=1,a2=1,an=a(n-1)+a(n-2)(n>=3,n∈N*)

斐波那契数列(Java)

一.什么是斐波那契数列 斐波那契数列(Fibonacci sequence),又称黄金分割数列.因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为"兔子数列",指的是这样一个数列:1,1,2,3,5,8,13,21,34,--1,1,2,3,5,8,13,21,34,--在数学上,斐波纳契数列以如下被以递推的方法定义:F(1)=1,F(2)=1,F(n)=F(n?1)+F(n?2)(n>=3,n∈N?) 二.Java实现(输出前

两种JS方法实现斐波那契数列

第一种方法:递归 function fibonacci(n){ if (n==0){ return 0; }else if (n==1){ return 1; } return fibonacci(n-1)+fibonacci(n-2); } 网上现有的方法是: function fibonacci(n){ if (n==1||n==2){ return 1; } return fibonacci(n-1)+fibonacci(n-2); } 这样做有个不足之处,n取值必须大于0. 第二种方法:

求斐波那契数列第n位的几种实现方式及性能对比(c#语言)

在每一种编程语言里,斐波那契数列的计算方式都是一个经典的话题.它可能有很多种计算方式,例如:递归.迭代.数学公式.哪种算法最容易理解,哪种算法是性能最好的呢? 这里给大家分享一下我对它的研究和总结:下面是几种常见的代码实现方式,以及各自的优缺点.性能对比. Iteration using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; public class Progr

斐波那契数列和反向计算问题

反向计算:编写一个函数将一个整型转换为二进制形式 反向计算问题,递归比循环更简单 分析:需要理解,奇数的二进制最后一位是1,偶数的二进制最后一位一定是0,联想记忆,这个和整型的奇偶性是一致的,1本身就是奇数,0本身是偶数. 十进制整数转换为二进制整数采用"除2取余,逆序排列"法. 具体做法是:用2整除十进制整数,可以得到一个商和余数,再用2去除商,又会得到一个商和余数,如此进行,直到商为0时为止,然后把先得到的余数作为二进制数的低位有效位,后得到的余数作为二进制数的高位有效位,依次排列

斐波那契数列实例讲解以及C++实现

斐波那契数列,又称黄金分割数列,指的是这样一个数列:0.1.1.2.3.5.8.13.21.--在数学上,斐波纳契数列以如下被以递归的方法定义:F(0)=0,F(1)=1,F(n)=F(n-1)+F(n-2)(n≥2,n∈N*)在现代物理.准晶体结构.化学等领域,斐波纳契数列都有直接的应用,为此,美国数学会从1963起出版了以<斐波纳契数列季刊>为名的一份数学杂志,用于专门刊载这方面的研究成果. 斐波那契数列指的是这样一个数列 0, 1, 1, 2, 3, 5, 8, 13, 21, 34,