算法笔记_001:斐波那契数的多种解法(Java)
本篇文章解决的问题来源于算法设计与分析课程的课堂作业,主要是运用多种方法来计算斐波那契数。具体问题及解法如下:
一、问题1:
问题描述:利用迭代算法寻找不超过编程环境能够支持的最大整数的斐波那契数是第几个斐波那契数。(Java: 231-1 for int, 263-1 for long)
解决方案:针对问题1,此处要使用迭代法来解决,具体实现代码如下:
//用迭代法寻找编程环境支持的最大整数(int型)的斐波那契数是第几个斐波那契数
public static int max_int_iteration(){
int a = 1,b = 1,c = 2;
int count = 3;
for( ;b < c; ){ //一旦c达到编程环境最大斐波那契数,便会产生内存溢出,从而变成一个负数,到此循环结束
a = b;
b = c;
c = a + b;
count++;
}
return count;
} //用迭代法寻找编程环境支持的最大整数(long型)的斐波那契数是第几个斐波那契数
public static long max_long_iteration(){
long a = 1,b = 1,c = 2;
long count = 3;
for( ;b<c; ){ //一旦c达到编程环境最大斐波那契数,便会产生内存溢出,从而变成一个负数,到此循环结束
a = b;
b = c;
c = a + b;
count++;
}
return count;
}
二、问题2:
问题描述:根据问题1中计算的最大的斐波那契数序号n,采用递归方式计算第n个斐波那契数,看其是否可以在3分钟内完成。
解决方案:针对问题2,此处要使用递归法来解决,问题1实际运行结果为:支持的最大整数(int型)值为47,支持的最大整数(long型)值为93。使用递归法计算第47个斐波那契数实际所需时间为138秒左右(此处本人使用WIN7系统运行所得),具体实现代码如下:
//递归法
public static long recursion(long n){
long result = 0; //最后一个斐波那契数及存储中间斐波那契数的变量
if(n <= 0)
result = 0;
if(n == 1 || n == 2)
result = 1;
if(n > 2)
{
result = recursion(n-1) + recursion(n-2);
//System.out.print(result+" ");
}
return result;
}
//具体实现就只需使用System.currentTimeMillis()方法获取系统当前时间,就可以你计算出计算第47个斐波那契数所需时间
三、问题3
问题描述:利用递归算法计算你的计算机能够在1,5,10,50秒内计算出的最大斐波那契数是第几个,利用迭代算法求解同样的问题。
解决方案:针对问题3,此处先要了解获得系统当前时间的相关方法及用法,然后计算规定时间内计算出的最大斐波那契数就很简单啦,具体实现代码如下:
//在1,5,10,50秒内使用迭代法算出的最大斐波那契数是第几个
public static void time_iteration(){
int a = 1,b = 1,c = 2;
long count = 3;
long a1 = 0,a2 = 0,a3 = 0,a4 = 0;
long t1 = System.currentTimeMillis();
long t2 = System.currentTimeMillis();
for( ;t2-t1 < 60000; ){
a = b;
b = c;
c = a + b;
count++;
t2 = System.currentTimeMillis();
if(t2-t1 == 1000)
a1 = count;
//System.out.println("1秒内最大斐波那契数是第:"+count+"个 ");
if(t2-t1 == 5000)
a2 = count;
//System.out.println("5秒内最大斐波那契数是第:"+count+"个 ");
if(t2-t1 == 10000)
a3 = count;
//System.out.println("10秒内最大斐波那契数是第:"+count+"个 ");
if(t2-t1 == 50000)
a4 = count;
//System.out.println("50秒内最大斐波那契数是第:"+count+"个 ");
}
System.out.println("迭代法1秒内最大斐波那契数是第:"+a1+"个 ");
System.out.println("迭代法5秒内最大斐波那契数是第:"+a2+"个 ");
System.out.println("迭代法10秒内最大斐波那契数是第:"+a3+"个 ");
System.out.println("迭代法50秒内最大斐波那契数是第:"+a4+"个 ");
} //递归法
public static long recursion(long n){
long result = 0; //最后一个斐波那契数及存储中间斐波那契数的变量
if(n <= 0)
result = 0;
if(n == 1 || n == 2)
result = 1;
if(n > 2)
{
result = recursion(n-1) + recursion(n-2);
//System.out.print(result+" ");
}
return result;
} //规定时间内,递归法计算出的最大斐波那契数是第几个
public static int recursion_time(long time){
long starttime_dg=System.currentTimeMillis();
int i=3;
long endtime_dg=0;
while(endtime_dg<starttime_dg+time*1000){
endtime_dg=System.currentTimeMillis();
i++;
recursion(i);
}
return i;
} //递归法在1,5,10,50秒内算出的最大斐波那契数是第几个
public static void fbnq_recursion_time(){ System.out.println("1秒内最大斐波那契数是第:"+recursion_time(1)+"个 "); System.out.println("5秒内最大斐波那契数是第:"+recursion_time(5)+"个 "); System.out.println("10秒内最大斐波那契数是第:"+recursion_time(10)+"个 "); System.out.println("50秒内最大斐波那契数是第:"+recursion_time(50)+"个 "); }
四、问题4
问题描述:利用公式F(n) = [fn/sqrt(5)]快速计算第n个斐波那契数,找出出现误差时的最小n值。其具体公式描述见下图:

解决方案:针对问题4,只需要将上述公式用代码来描述就可以完成,具体实现代码如下:
//直接求值法(利用公式F(n) = [@n/sqrt(5)]快速计算第n个斐波那契数)
public static double formula(int n){
double result = 0;
double temp = Math.sqrt(5.0);
result = (1/temp)*(Math.pow((1+temp)/2,n)-Math.pow((1-temp)/2, n));
return result;
}
五、问题5
问题描述:利用矩阵相乘方法计算第n个斐波那契数。
解决方案:对于矩阵相乘法,首先得对矩阵相乘的法则要熟悉,现简单说明一下矩阵相乘求斐波那契数的思想及原理:
(1)矩阵定义
的矩阵C为矩阵A与B的乘积,记作


(2)矩阵相乘求斐波那契数原理

进一步,可以得出直接推导公式:

具体实现代码如下:
// 关联矩阵
private static final int[][] UNIT = { { 1, 1 }, { 1, 0 } }; //定义一个上面公式中需要计算的二维数组
// 全0矩阵
private static final int[][] ZERO = { { 0, 0 }, { 0, 0 } }; //定义一个元素均为0的二维数组
/**
* 求斐波那契数列
*
* @param n
* @return
*/
public static int[][] fb(int n) {
if (n == 0) { //指数n为0时返回该数组
return ZERO;
}
if (n == 1) { //指数n为1时返回该数组
return UNIT;
}
// n是偶数
if ((n & 1) == 0) { //把(n&1) == 0换成(n%2) == 0等价 , 唯一区别在于(n&1) == 0计算效率高
int[][] matrix = fb(n >> 1); //n >> 1意思是指将n的二进制数向右移动1位,最高位补0。相当于把n除以2
return matrixMultiply(matrix, matrix);
}
// n是奇数
int[][] matrix = fb((n - 1) >> 1);
return matrixMultiply(matrixMultiply(matrix, matrix), UNIT);
} /**
* 矩阵相乘
*
* @param m
* r1*c1
* @param n
* c1*c2
* @return 新矩阵,r1*c2
*/
public static int[][] matrixMultiply(int[][] m, int[][] n) {
int rows = m.length;
int cols = n[0].length;
int[][] r = new int[rows][cols];
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
r[i][j] = 0;
for (int k = 0; k < m[i].length; k++) {
r[i][j] += m[i][k] * n[k][j];
}
}
}
return r;
} //具体实现矩阵相乘算法
public static int matrix(int n){
int[][] m = fb(n);
return m[0][1];
}
六、问题6
问题描述:对于相同的输入n值,比较上述四种方法的基本操作次数,以掌握对数、线性和指数增长率的极大差别。
解决方案:此处本人只是把上述所用的四种方法运行了一遍,计算各个相应方案计算第n个斐波那契数所花时间,因为具体算法相应的基本操作次数,用代码来计算有点麻烦,所以此处就不罗列了。
七、新算法法
这是解决斐波那契数的一种新算法,该算法的时间和空间效率都优于上面四种算法,该算法所用公式如下图所示:

具体实现代码如下:
//新算法法
public static int new_way(int n){
//int a = 1,b = 1,c = 2,d = 3;
int result = 0; //定义最后一个斐波那契数
//根据输入n,求出最后一个斐波那契数
if(n == 0)
result = 0;
else if(n == 1 || n == 2)
result = 1;
else if(n == 3)
result = 2;
else if(n >= 4){ //若n大于4返回resul
int a1 = n/4;
int b1 = n%4;
int a = new_way(a1);
int b = new_way((a1+1));
int c = new_way((a1-1));
int d = new_way((a1+2));
if(b1 == 0)
result = (int) ((Math.pow(b,2) - Math.pow(c,2))*(Math.pow(c, 2) + 2*Math.pow(a, 2) + Math.pow(b,2)));
if(b1 == 1)
result = (int) (Math.pow((Math.pow(b,2) - Math.pow(c,2)),2) + Math.pow((Math.pow(a, 2) + Math.pow(b,2)),2));
if(b1 == 2)
result = (int) ((Math.pow(a, 2) + Math.pow(b,2))*(3*Math.pow(b,2)+Math.pow(a, 2)-2*Math.pow(c,2)));
if(b1 == 3)
result = (int) (Math.pow((Math.pow(a, 2) + Math.pow(b,2)),2) + Math.pow((Math.pow(d,2)-Math.pow(a,2)),2)); }
return result;
}
八、具体类运行实现
代码如下:
 package com.liuzhen.ex_one;
 public class Fibonacci {
     //迭代法
     public static int iteration(int n){   /*此处(包含下面所有方法)声明为静态方法,原因是在本类main()方法中调用
         类中方法,对于一般的非static成员变量或方法,需要有一个对象的实例才能调用,所以要先生成对象的实例,他们才会实际的分配内存空间。
         而对于static的对象或方法,在程序载入时便已经分配了内存空间,他只和特定的类想关联,无需实例化  */
         int result = 1;  //最后一个斐波那契数
         int a[] = new int[n+1];    //存放斐波那契数,初始值为空,默认全为0
         a[0] = 0;
         a[1] = 1;
         //System.out.println("迭代法计算斐波那契数结果:");
         //System.out.print(a[0]+"  "+a[1]+"  ");
         for(int i = 2;i < n+1;i++){
             a[i] = a[i-1] + a[i-2];
             //result = a[i];
             //System.out.print(result+"  ");        //打印斐波那契数
         }
         //System.out.println();
         result=a[n];
         return result;    //返回最后一个斐波那契数
     }
     //用迭代法寻找编程环境支持的最大整数(int型)的斐波那契数是第几个斐波那契数
     public static int max_int_iteration(){
         int a = 1,b = 1,c = 2;
         int count = 3;
         for( ;b < c; ){   //一旦c达到编程环境最大斐波那契数,便会产生内存溢出,从而变成一个负数,到此循环结束
             a = b;
             b = c;
             c = a + b;
             count++;
         }
         return count;
     }
     //用迭代法寻找编程环境支持的最大整数(long型)的斐波那契数是第几个斐波那契数
     public static long max_long_iteration(){
         long a = 1,b = 1,c = 2;
         long count = 3;
         for( ;b<c; ){    //一旦c达到编程环境最大斐波那契数,便会产生内存溢出,从而变成一个负数,到此循环结束
             a = b;
             b = c;
             c = a + b;
             count++;
         }
         return count;
     }
     //在1,5,10,50秒内使用迭代法算出的最大斐波那契数是第几个
         public static void time_iteration(){
             int a = 1,b = 1,c = 2;
             long count = 3;
             long a1 = 0,a2 = 0,a3 = 0,a4 = 0;
             long t1 = System.currentTimeMillis();
             long t2 = System.currentTimeMillis();
             for( ;t2-t1 < 60000; ){
                 a = b;
                 b = c;
                 c = a + b;
                 count++;
                 t2 = System.currentTimeMillis();
                 if(t2-t1 == 1000)
                     a1 = count;
                     //System.out.println("1秒内最大斐波那契数是第:"+count+"个 ");
                 if(t2-t1 == 5000)
                     a2 = count;
                     //System.out.println("5秒内最大斐波那契数是第:"+count+"个 ");
                 if(t2-t1 == 10000)
                     a3 = count;
                     //System.out.println("10秒内最大斐波那契数是第:"+count+"个 ");
                 if(t2-t1 == 50000)
                     a4 = count;
                     //System.out.println("50秒内最大斐波那契数是第:"+count+"个 ");
             }
             System.out.println("迭代法1秒内最大斐波那契数是第:"+a1+"个 ");
             System.out.println("迭代法5秒内最大斐波那契数是第:"+a2+"个 ");
             System.out.println("迭代法10秒内最大斐波那契数是第:"+a3+"个 ");
             System.out.println("迭代法50秒内最大斐波那契数是第:"+a4+"个 ");
         }
     //递归法
     public static long recursion(long n){
         long result = 0;   //最后一个斐波那契数及存储中间斐波那契数的变量
         if(n <= 0)
             result = 0;
         if(n == 1 || n == 2)
             result = 1;
         if(n > 2)
         {
             result = recursion(n-1) + recursion(n-2);
             //System.out.print(result+"  ");
         }
         return result;
     }
     //规定时间内,递归法计算出的最大斐波那契数是第几个
     public static int recursion_time(long time){
         long starttime_dg=System.currentTimeMillis();
         int i=3;
         long endtime_dg=0;
         while(endtime_dg<starttime_dg+time*1000){
         endtime_dg=System.currentTimeMillis();
         i++;
         recursion(i);
         }
         return i;
         }
     //递归法在1,5,10,50秒内算出的最大斐波那契数是第几个
     public static void fbnq_recursion_time(){
               System.out.println("1秒内最大斐波那契数是第:"+recursion_time(1)+"个 ");
               System.out.println("5秒内最大斐波那契数是第:"+recursion_time(5)+"个 ");
               System.out.println("10秒内最大斐波那契数是第:"+recursion_time(10)+"个 ");
               System.out.println("50秒内最大斐波那契数是第:"+recursion_time(50)+"个 ");
         }
     //测试递归法在1,5,10,50秒内使用迭代法算出的最大斐波那契数是第几个
     public static void time_recursion_test(){
         long t1 = System.currentTimeMillis();
         long t2 = 0;
         int i = 3;
         for(;t2-t1 > 60000;){
             recursion(i);
             i++;
             t2 = System.currentTimeMillis();
             if(t2-t1 == 1000)
                 System.out.println("1秒内最大斐波那契数是第:"+i+"个 ");
             if(t2-t1 == 5000)
                 System.out.println("5秒内最大斐波那契数是第:"+i+"个 ");
             if(t2-t1 == 10000)
                 System.out.println("10秒内最大斐波那契数是第:"+i+"个 ");
               if(t2-t1 == 50000)
                 System.out.println("50秒内最大斐波那契数是第:"+i+"个 ");
         }
     }
     //直接求值法(利用公式F(n) = [@n/sqrt(5)]快速计算第n个斐波那契数)
     public static double formula(int n){
         double result = 0;
         double temp = Math.sqrt(5.0);
         result =  (1/temp)*(Math.pow((1+temp)/2,n)-Math.pow((1-temp)/2, n));
         return result;
     }
     //利用直接求值法,出现误差时最小的n值
     public static int min_formula(){
         double result_fn=1;
         int i=1;
         while(result_fn-(double)iteration(i)<1){
         result_fn=formula(i);
         i++;
         }
         return i;
     }
     //新算法法
     public static int new_way(int n){
         //int a = 1,b = 1,c = 2,d = 3;
         int result = 0;   //定义最后一个斐波那契数
         //根据输入n,求出最后一个斐波那契数
         if(n == 0)
             result = 0;
         else if(n == 1 || n == 2)
             result =  1;
         else if(n == 3)
             result =  2;
         else if(n >= 4){    //若n大于4返回resul
             int a1 = n/4;
             int b1 = n%4;
             int a = new_way(a1);
             int b = new_way((a1+1));
             int c = new_way((a1-1));
             int d = new_way((a1+2));
             if(b1 == 0)
                 result = (int) ((Math.pow(b,2) - Math.pow(c,2))*(Math.pow(c, 2) + 2*Math.pow(a, 2) + Math.pow(b,2)));
             if(b1 == 1)
                 result = (int) (Math.pow((Math.pow(b,2) - Math.pow(c,2)),2) + Math.pow((Math.pow(a, 2) + Math.pow(b,2)),2));
             if(b1 == 2)
                 result = (int) ((Math.pow(a, 2) + Math.pow(b,2))*(3*Math.pow(b,2)+Math.pow(a, 2)-2*Math.pow(c,2)));
             if(b1 == 3)
                 result = (int) (Math.pow((Math.pow(a, 2) + Math.pow(b,2)),2) + Math.pow((Math.pow(d,2)-Math.pow(a,2)),2));
         }
             return result;
     }
     // 关联矩阵
     private static final int[][] UNIT = { { 1, 1 }, { 1, 0 } };
     // 全0矩阵
     private static final int[][] ZERO = { { 0, 0 }, { 0, 0 } };
     /**
      * 求斐波那契数列
      *
      * @param n
      * @return
      */
     public static int[][] fb(int n) {
         if (n == 0) {
             return ZERO;
         }
         if (n == 1) {
             return UNIT;
         }
         // n是奇数
         if ((n & 1) == 0) {
             int[][] matrix = fb(n >> 1);
             return matrixMultiply(matrix, matrix);
         }
         // n是偶数
         int[][] matrix = fb((n - 1) >> 1);
         return matrixMultiply(matrixMultiply(matrix, matrix), UNIT);
     }  
     /**
      * 矩阵相乘
      *
      * @param m
      *            r1*c1
      * @param n
      *            c1*c2
      * @return 新矩阵,r1*c2
      */
     public static int[][] matrixMultiply(int[][] m, int[][] n) {
         int rows = m.length;
         int cols = n[0].length;
         int[][] r = new int[rows][cols];
         for (int i = 0; i < rows; i++) {
             for (int j = 0; j < cols; j++) {
                 r[i][j] = 0;
                 for (int k = 0; k < m[i].length; k++) {
                     r[i][j] += m[i][k] * n[k][j];
                 }
             }
         }
         return r;
     }  
     //具体实现矩阵相乘算法
     public static int matrix(int n){
         int[][] m = fb(n);
         return m[0][1];
     }
     public static void main(String[] args){
         System.out.print(max_int_iteration());
         System.out.println();
         System.out.print(max_long_iteration());
         System.out.println();
         System.out.println();
         long t1 = System.currentTimeMillis();
         long a = recursion(47);
         long t2 = System.currentTimeMillis();
         System.out.println("递归法求斐波那契数:");
         System.out.println(a);
         System.out.println("递归算法Time is: " + (t2 - t1)/1000.0+"秒");
         //此处下面可以直接通过给相关类传递参数,实现相应功能,上面的中代码仅仅提供示例
     }
 }
PS:运行部分结果截图

对于本问题,我自己用安卓做了一个简单的展示界面(具体介绍请参考我的另一篇博客:用安卓实现斐波那契数和最近点对问题):



参考资料:
1、斐波那契数列解析
算法笔记_001:斐波那契数的多种解法(Java)的更多相关文章
- hdu1316(大数的斐波那契数)
		
题目信息:求两个大数之间的斐波那契数的个数(C++/JAVA) pid=1316">http://acm.hdu.edu.cn/showproblem.php? pid=1316 这里 ...
 - 数学算法(一):快速求斐波那契数第n项通过黄金分割率公式
		
有一个固定的数学公式= =,不知道的话显然没法应用 首先黄金分割率接近于这个公式, (以下为黄金分割率与斐波那契的关系,可跳过) 通过斐波那契数列公式 两边同时除以 得: (1) 注意后一项比前一项接 ...
 - LeetCode.509——斐波那契数
		
问题描述: 斐波那契数,通常用 F(n) 表示,形成的序列称为斐波那契数列.该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和.也就是: F(0) = 0, F(1) = 1 F(N) ...
 - 用x种方式求第n项斐波那契数,99%的人只会第一种
		
大家好啊,我们又见面了.听说有人想学数据结构与算法却不知道从何下手?那你就认真看完本篇文章,或许能从中找到方法与技巧. 本期我们就从斐波那契数列的几种解法入手,感受算法的强大与奥妙吧. 原文链 ...
 - UVA 11582 Colossal Fibonacci Numbers! 大斐波那契数
		
大致题意:输入两个非负整数a,b和正整数n.计算f(a^b)%n.其中f[0]=f[1]=1, f[i+2]=f[i+1]+f[i]. 即计算大斐波那契数再取模. 一开始看到大斐波那契数,就想到了矩阵 ...
 - 斐波那契数[XDU1049]
		
Problem 1049 - 斐波那契数 Time Limit: 1000MS Memory Limit: 65536KB Difficulty: Total Submit: 1673 Ac ...
 - C++求斐波那契数
		
题目内容:斐波那契数定义为:f(0)=0,f(1)=1,f(n)=f(n-1)+f(n-2)(n>1且n为整数) 如果写出菲氏数列,则应该是: 0 1 1 2 3 5 8 13 21 34 …… ...
 - Project Euler 104:Pandigital Fibonacci ends 两端为全数字的斐波那契数
		
Pandigital Fibonacci ends The Fibonacci sequence is defined by the recurrence relation: F[n] = F[n-1 ...
 - DP:斐波纳契数
		
题目:输出第 n 个斐波纳契数(Fibonacci) 方法一.简单递归 这个就不说了,小n怡情,大n伤身啊……当n=40的时候,就明显感觉到卡了,不是一般的慢. //输出第n个 Fibonacci 数 ...
 
随机推荐
- DEX 可视化查阅
			
参考: http://bbs.pediy.com/thread-208828.htm 010 Editor 下载地址: http://www.sweetscape.com/download/ //-- ...
 - IIS Express并发数设置
			
今天将之前的一个瓦片图的服务迁移到了asp.net core试了一下,使用的时候感觉客户端刷新时有些慢,估计是并发连接数限制的原因. 由于这是一个开发中的版本,是用IIS Express部署的,IIS ...
 - LNMP 1.2升级Nginx、MySQL/MariaDB、PHP教程
			
一般情况下不建议对生产环境进行升级,升级开始后会停止LNMP相关服务.本文仅适用于LNMP1.2及以后版本! 在LNMP目前LNMP v1.2中已经包含了Nginx.MySQL/MariaDB.PHP ...
 - Tasker to answer incoming call by pressing power button
			
nowadays, the smartphone is getting bigger in size, eg. samsung galaxy note and note 2, sorta big in ...
 - Tasker to stop Poweramp control for the headset while there is an incoming SMS - frozen
			
If you usually like to use Poweramp or any other media player to enjoy the music with headset plugge ...
 - Bus Blaster
			
http://dangerousprototypes.com/docs/Bus_Blaster Bus Blaster v2 is an experimental, high-speed JTAG d ...
 - Html.BeginForm() vs Ajax.BeginForm() in MVC3
			
我们知道,BeginForm()方法能创建一个Form标签,因此可以结合表单级的方法,在这个页面中.我一直在考虑Html.BeginForm()方法和Ajax.BeginForm()方法在MVC3中有 ...
 - vim 之cscope的使用
			
http://www.mcuos.com/thread-8488-1-1.html http://blog.csdn.net/longerzone/article/details/7789581 ht ...
 - 【docker】docker容器和宿主机之间文件互传,互相拷贝
			
原文地址:https://www.cnblogs.com/areyouready/p/8973495.html [注意:命令中符号均为英文符号] 1.从容器里面拷文件到宿主机 答:在宿主机里面执行以下 ...
 - Furure的简单介绍和使用
			
引子: