java实现斐波那契数列求解办法

Posted luffy5459

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java实现斐波那契数列求解办法相关的知识,希望对你有一定的参考价值。

    斐波那契数列最早是根据兔子繁殖问题而产生的,大致有这样的一个数列:1,1,2,3,5,8,13,。。。,其中第一、第二项固定为1,后面每一项都是前面两项之和。使用数学公式就是f(n) = f(n-1)+f(n-2)。

     直观来看,最简单的做法就是通过递归求解。

public static long fib1(int n) {
	if(n<1)
		return -1;
	if(n==1||n==2)
		return 1;
	return fib1(n-2)+fib1(n-1);
}

     这种算法,时间复杂度为O(n*n)。因为递归算法,需要分解和回归,两次都很耗时间。而且随着n的增大,这个算法时间复杂度会指数级增长。

      接着,算法改进。这个数列,其实每一项,通过计算都是可以得到的,我们从1到n,依次计算f(n),然后将计算结果存入一个数组(这里需要申请一个数组存储结算结果),最后返回f(n)的结果,问题就解决了。

public static long fib2(int n) {
	if(n<1)
		return -1;
	long[] data = new long[n+1];
	data[1] = 1;
	data[2] = 1;
	for(int i=3;i<=n;i++)
		data[i] = data[i-1] + data[i-2];
	return data[n];
}

    算法复杂度一下子就发生了改变,从O(n*n)变为O(n),但是空间复杂度增加了,因为申请了数组。 

    接着,算法还可以改进,因为我们只需要求最后的结果,而且我们借助一个中间变量,保存最新计算的结果,不需要申请数组。这种算法也叫动态规划算法。就是迭代计算每个结果,直到计算出n的结果。

public static long fib3(int n) {
	long i,a,b;
	if(n<1)
		return -1;
	if(n==1||n==2)
		return 1;
	a = 1;
	b = 1;
	for(i=3;i<=n;i++) {
		b = a + b;
		a = b - a;
	}
	return b;
}

 时间复杂度还是O(n),而空间复杂度变为了O(1)。

 这里还有一种办法,就是直接根据公式计算,这里的计算公式:

public static long fib4(int n) {
	double result = 0;
	double sqrt5 = Math.sqrt(5);
	result = (Math.pow((1+sqrt5)/2, n)-Math.pow((1-sqrt5)/2,n))/sqrt5;
	return (long)result;
}

    这个算法并不通用,主要在于71之后,因为计算机精度问题,导致不准确。71以前的还可以。后面可以贴出运行结果。

    最后一种办法就是在时间复杂度上更进一步,直接变为log(n)。但是算法的推导很复杂。用到了矩阵,矩阵乘法,矩阵快速幂运算。有一些巧妙的手段。

    公式推导如下:

    

              

   第一个公式是推导公式,第二个根据自身做了一个变换。

   按照矩阵公式,可以得到如下的结论:

 将矩阵做个变换:

再变换:

这里涉及到矩阵快速幂的计算:

这个公式里面,当n为奇数的时候,似乎是不太对,好像多乘了。这里其实取了一个巧,利用计算机整数作除法结果为整数的规律。这里假设 A = 2,n = 9 那么 2^9 = 2^8*2 = 2 ^ (( 9/2)*2) * 2,因为在计算机整数除法中 8 = (9/2)*2

    这里,就是需要计算:

的结果,

如果:

,那么,fn = b。所以代码中会有这样的表示,最后返回result[1][0],这里result是一个两行一列的二维数组,直接返回第二行,第一列。

矩阵快速幂里面,引入了,这里值为0。

上面提到的快速幂,在代码中,采用了一种降幂的计算方法。无论n等于多少,最后,都会成某一个数的2次幂、1次幂。

public static long fib5(int n) {
	if(n<1)
		return -1;
	if(n==1||n==2)
		return 1;
	long[][] result = {{1},{0}};
	long[][] tem = {{1,1},{1,0}};
	while(n!=0) {
		if((n&1)==1)
			result = matrixMultiply(tem, result);
		tem = matrixMultiply(tem, tem);
		n>>=1;
	}
	return result[1][0];
}

public static long[][] matrixMultiply(long[][] a,long[][] b){
	int rows = a.length;
	int cols = b[0].length;
	long[][] matrix = new long[rows][cols];
	
	for(int i=0;i<rows;i++) {
		for(int j=0;j<cols;j++) {
			for(int k=0;k<a[i].length;k++) {
				matrix[i][j] += a[i][k] * b[k][j];
			}
		}
	}
	
	return matrix;
}

 完整算法:

package com.xxx.huali.hualitest.fibonacci;

public class Main {
	
	/***
	 * 递归思想
	 * 时间复杂度O(n*n)
	 * @param n
	 * @return
	 */
	public static long fib1(int n) {
		if(n<1)
			return -1;
		if(n==1||n==2)
			return 1;
		return fib1(n-2)+fib1(n-1);
	}
	
	/**
	 * 引入一个数组,计算每一项的值
	 * 时间复杂度O(n)
	 * 空间复杂度O(n)
	 * @param n
	 * @return
	 */
	public static long fib2(int n) {
		if(n<1)
			return -1;
		long[] data = new long[n+1];
		data[1] = 1;
		data[2] = 1;
		for(int i=3;i<=n;i++)
			data[i] = data[i-1] + data[i-2];
		return data[n];
	}
	
	/***
	 * 动态规划思想,迭代计算
	 * 时间复杂度O(n)
	 * 空间复杂度O(1)
	 * @param n
	 * @return
	 */
	public static long fib3(int n) {
		long i,a,b;
		if(n<1)
			return -1;
		if(n==1||n==2)
			return 1;
		a = 1;
		b = 1;
		for(i=3;i<=n;i++) {
			b = a + b;
			a = b - a;
		}
		return b;
	}
	
	/***
	 * 根据推导公式计算
	 * 特征方程x^2 = x+1
	 * @param n
	 * @return
	 */
	public static long fib4(int n) {
		double result = 0;
		double sqrt5 = Math.sqrt(5);
		result = (Math.pow((1+sqrt5)/2, n)-Math.pow((1-sqrt5)/2,n))/sqrt5;
		return (long)result;
	}
	
	/***
	 * 矩阵快速幂算法
	 * 时间复杂度O(logn)
	 * @param n
	 * @return
	 */
	public static long fib5(int n) {
		if(n<1)
			return -1;
		if(n==1||n==2)
			return 1;
		long[][] result = {{1},{0}};
		long[][] tem = {{1,1},{1,0}};
		while(n!=0) {
			if((n&1)==1)
				result = matrixMultiply(tem, result);
			tem = matrixMultiply(tem, tem);
			n>>=1;
		}
		return result[1][0];
	}
	
	public static long[][] matrixMultiply(long[][] a,long[][] b){
		int rows = a.length;
		int cols = b[0].length;
		long[][] matrix = new long[rows][cols];
		
		for(int i=0;i<rows;i++) {
			for(int j=0;j<cols;j++) {
				for(int k=0;k<a[i].length;k++) {
					matrix[i][j] += a[i][k] * b[k][j];
				}
			}
		}
		
		return matrix;
	}
	
	public static void main(String[] args) {
		int n = 71;
		//System.out.println(fib1(n));
		System.out.println(fib2(n));
		System.out.println(fib3(n));
		System.out.println(fib4(n));
		System.out.println(fib5(n));
		System.out.println("---------------");
		n = 72;
		//System.out.println(fib1(n));
		System.out.println(fib2(n));
		System.out.println(fib3(n));
		System.out.println(fib4(n));
		System.out.println(fib5(n));
	}
}

运行程序,打印结果如下,通过结果也能看出n=71以及n=72时,通过公式计算的结果确实有问题。       

以上是关于java实现斐波那契数列求解办法的主要内容,如果未能解决你的问题,请参考以下文章

计算斐波那契数列的性能对比:Python,Java,Go

谁能帮我用JAVA编写一个斐波那契数列,用eclipse实现,代码不对不采纳!

用递归和非递归方法求解斐波那契数列

求解斐波那契数列复杂度分析

如何用java语言输出斐波那契数列

斐波那契数列java实现