本篇文章解决的问题来源于算法设计与分析课程的课堂作业,主要是运用多种方法来计算斐波那契数。具体问题及解法如下:

一、问题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)矩阵定义

A

mxp的矩阵,B
pxn的矩阵,那么称
mxn

的矩阵C为矩阵AB的乘积,记作

C =AB,其中矩阵C中的第
i行第
j列元素可以表示为:
如下所示:
 

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

数列的递推公式为:f(1)=1,f(2)=1,f(3)=2 , f(n)=f(n-1)+f(n-2)(n>=3)用矩阵表示为:

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

具体实现代码如下:

// 关联矩阵
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、斐波那契数列解析

2、斐波那契数列的矩阵解法(java实现)

算法笔记_001:斐波那契数的多种解法(Java)的更多相关文章

  1. hdu1316(大数的斐波那契数)

    题目信息:求两个大数之间的斐波那契数的个数(C++/JAVA) pid=1316">http://acm.hdu.edu.cn/showproblem.php? pid=1316 这里 ...

  2. 数学算法(一):快速求斐波那契数第n项通过黄金分割率公式

    有一个固定的数学公式= =,不知道的话显然没法应用 首先黄金分割率接近于这个公式, (以下为黄金分割率与斐波那契的关系,可跳过) 通过斐波那契数列公式 两边同时除以 得: (1) 注意后一项比前一项接 ...

  3. LeetCode.509——斐波那契数

    问题描述: 斐波那契数,通常用 F(n) 表示,形成的序列称为斐波那契数列.该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和.也就是: F(0) = 0, F(1) = 1 F(N) ...

  4. 用x种方式求第n项斐波那契数,99%的人只会第一种

    大家好啊,我们又见面了.听说有人想学数据结构与算法却不知道从何下手?那你就认真看完本篇文章,或许能从中找到方法与技巧.     本期我们就从斐波那契数列的几种解法入手,感受算法的强大与奥妙吧. 原文链 ...

  5. 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]. 即计算大斐波那契数再取模. 一开始看到大斐波那契数,就想到了矩阵 ...

  6. 斐波那契数[XDU1049]

    Problem 1049 - 斐波那契数 Time Limit: 1000MS   Memory Limit: 65536KB   Difficulty: Total Submit: 1673  Ac ...

  7. 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 …… ...

  8. Project Euler 104:Pandigital Fibonacci ends 两端为全数字的斐波那契数

    Pandigital Fibonacci ends The Fibonacci sequence is defined by the recurrence relation: F[n] = F[n-1 ...

  9. DP:斐波纳契数

    题目:输出第 n 个斐波纳契数(Fibonacci) 方法一.简单递归 这个就不说了,小n怡情,大n伤身啊……当n=40的时候,就明显感觉到卡了,不是一般的慢. //输出第n个 Fibonacci 数 ...

随机推荐

  1. SMACH(五)----用户数据UserData类和重映射Remapper类的原理和例子

    用户数据UserData类和重映射Remapper类包含在smach中的user_data.py文件中实现,该博文主要介绍其原理和例子 UserData主要用于状态之间的数据传递,包括数据的输入inp ...

  2. SyncTrayzor -- Windows tray utility / filesystem watcher / launcher for syncthing

    SyncTrayzor is a little tray utility for Syncthing on Windows. It hosts and wraps Syncthing, making ...

  3. Tasker to auto record incoming or outgoing call

    Tasker to auto record incoming or outgoing call most of time, i was thinking of tasker can do the jo ...

  4. MYSQL 源码- 淘宝数据库研发组

    http://mysql.taobao.org/index.php?title=%E8%B5%84%E6%96%99%E5%85%B1%E4%BA%AB

  5. 达芬奇TI DVSDK之视频数据流过程分析

    作者:openwince@gmail.com 博客:http://www.cnblogs.com/tinz    本文的copyright归openwince@gmail.com所有,使用GPL发布, ...

  6. ASP.NET web.config中<customErrors>节点说明

    customErrors>节点用于定义一些自定义错误信息的信息.此节点有Mode和defaultRedirect两个属性,其中defaultRedirect属性是一个可选属性,表示应用程序发生错 ...

  7. linux 查找文件命令

    find -name 文件名    在当前目录下查找 find -name nginx.conf

  8. Design Pattern Singleton 单一模式

    单一模式的几个注意点: 一) 设计单一模式,首先须要把构造函数给私有化了,不让外界訪问,那么外界仅仅能通过提供的函数获取一个新的类. 二) C++的单一模式,记得要在类外初始化一个类,否则或内存出错的 ...

  9. CHM编写软件

    工具选择 1.         HTML编辑工具:就是用什么软件写文档的问题.一直都是做.NET开发,所以选择HTML编辑也大都在MS阵营里选.主要有以下几种HTML编辑器. (1)       Dr ...

  10. Spark Streaming的编程模型

    Spark Streaming的编程和Spark的编程如出一辙,对于编程的理解也非常类似.对于Spark来说,编程就是对于RDD的操作:而对于Spark Streaming来说,就是对DStream的 ...