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)。

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

f(n) = \frac{1}{\sqrt{5}}((\frac{1+\sqrt{5}}{2})^n-(\frac{1-\sqrt{5}}{2})^n)

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)。但是算法的推导很复杂。用到了矩阵,矩阵乘法,矩阵快速幂运算。有一些巧妙的手段。

    公式推导如下:

    f_{n} = f_{n-1} + f_{n-2}

   f_{n-1} = f_{n-1} + 0 * f_{n-2}           

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

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

\begin{pmatrix} f_{n},f_{n-1} \end{pmatrix} =\begin{pmatrix}f_{n-1},f_{n-2} \end{} * \begin{pmatrix} 1,1\\ 1, 0 \end{pmatrix}

 将矩阵做个变换:

\begin{pmatrix} f_{n}\\ f_{n-1} \end{} = \begin{pmatrix} 1,1\\ 1,0 \end{} * \begin{pmatrix} f_{n-1}\\ f_{n-2} \end{}

再变换:

\begin{pmatrix} f_{n+1}\\f_{n} \end{} = \begin{pmatrix} 1,1\\ 1,0 \end{} * \begin{pmatrix} f_{n}\\ f_{n-1} \end{} =\begin{pmatrix} 1,1 \\ 1,0 \end{} ^ n * \begin{pmatrix} f_{1} \\ f_{0} \end{}

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

A^n = \begin{cases} A & n=1 \\ (A^\frac{n}{2})^2 & n = even\\ (A ^ {\mid \frac{n}{2}\mid}) ^ 2 * A & n = odd \end{}

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

    这里,就是需要计算:

\begin{pmatrix} f_{n+1}\\f_{n} \end{} = \begin{pmatrix} 1,1\\ 1,0 \end{} * \begin{pmatrix} f_{n}\\ f_{n-1} \end{} =\begin{pmatrix} 1,1 \\ 1,0 \end{} ^ n * \begin{pmatrix} f_{1} \\ f_{0} \end{}的结果,

如果:

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

矩阵快速幂里面,引入了f_{0},这里值为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时,通过公式计算的结果确实有问题。       

  • 9
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

luffy5459

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值