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实现斐波那契数列求解办法的主要内容,如果未能解决你的问题,请参考以下文章