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

一、问题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. 如何从Windows远程上传文件到Linux(例如CentOS 7)

    一.先看Linux系统是否安装有vsftp软件(vs是very secure的意思) [root@localhost /]# rpm -qa | grep vsftpdvsftpd-3.0.2-9.e ...

  2. 《TCP/IP具体解释卷2:实现》笔记--ICMP:Internet控制报文协议

    ICMP在IP系统间传递差错和管理报文,是不论什么IP实现必须和要求的组成部分.能够把ICMP分成两类:差错和查询.查询报文 是用一对请求和回答定义的.差错报文通常包括了引起错误的IP包的第一个分片的 ...

  3. 模拟登陆CSDN——就是这么简单

    工具介绍 本篇文章主要是解说怎样模拟登陆CSDN.使用的工具是HttpClient+Jsoup 当中HttpClient主要是负责发送请求,而Jsoup主要是解析HTML 你可能对HttpClient ...

  4. Material Design(原质化设计)视觉设计语言规范 踏得网镜像

    Android 5.0 Lollipop(棒棒糖,也就是之前的代称Android L)全面实践了谷歌最新研发的 Material Design 设计语言规范,只是该设计规范并不是仅针对移动平台. 我们 ...

  5. MVC文件上传04-使用客户端jQuery-File-Upload插件和服务端Backload组件实现多文件异步上传

    本篇使用客户端jQuery-File-Upload插件和服务端Badkload组件实现多文件异步上传.MVC文件上传相关兄弟篇: MVC文件上传01-使用jquery异步上传并客户端验证类型和大小  ...

  6. SHP文件合并

    ArcGIS中合并SHP文件是一个常用的操作,下面简要讲解一下如何合并. 使用ArcGIS Tool Box(ArcGIS工具箱)中的Data Management Tools-->Genera ...

  7. Python中函数的参数传递与可变长参数

    转自旭东的博客原文 Python中函数的参数传递与可变长参数 Python中传递参数有以下几种类型: (1)像C++一样的默认缺省函数 (2)根据参数名传参数 (3)可变长度参数 示例如下: (1)默 ...

  8. iOS appStore中的应用 实现升级功能

    .h文件中 <UIAlertViewDelegate> .m文件中 #import "SBJson.h"        //解析sbjson 数据 - (void)vi ...

  9. 在Windows Phone项目中调用C语言DLL

    在Windows Phone项目中调用C语言写的DLL 最近接到一个需求,需要在WP里调用一个C语言写的DLL,并且说Android和iOS都可以,问我WP是否可以这样? 我说我调研一下,就有了下面的 ...

  10. Linux架构和目录-基础篇

    1.Linux目录结构 2. /boot/ 存放系统内核文件,如vmlinuz,initrd,System.map等.其中, a. vmlinuz是可引导的.压缩的内核,“vm”即“Virtual M ...