Fibonacci数列的定义如下(看百度百科):
F0=0,F1=1,Fn=F(n-1)+F(n-2)(n>=2,n∈N*)
传统方法
首先先看一个传统的基于递归的实现:
public static int fibonacci1(int i) {
if (i < 0) {
throw new IllegalArgumentException("The number should be not less than 0");
}
if (i < 2) {
return i;
}
return fibonacci1(i - 1) + fibonacci1(i - 2);
}
它有几个明显的问题:
1. 占用空间大
每一次递归的跟进,都会在调用栈上压入当前方法的栈帧。如果递归层次太深就会发生 java.lang.StackOverflowError 错误。例如在我的机器上,当调用 fibonacci1(100000) 时就会发生这种错误(应该更早就发生了溢出了)。
2. 效率低
除了占用空间大,效率低也是一个问题。效率低体现在几方法:
- 方法调用
太多的方法调用本向效率就不高
- 重复多
存在太多的重复计算是影响效率的一个方面。例如fibonacci(4)的计算,当计算fibonacci(5)时需要计算,计算fibonacci(6)的时候又要被计算一次。并且这种重复会产生链式反应,因为每一次的计算又是一次递归。
3. 结果不可靠
最后一个问题就是结果不可靠。fibonacci 数列的值增长很快,很快就能达到 java 中 int 能表示的最大值 (fibonacci(46)),并且也很快就能达到long能表达的数值的上限。也就是说上述实现,最多能计算到46,到47的结果就是不可靠的。
解决方案
使用循环
先来解决问题1和问题2。
通常,每一个递归实现都会有一个相应的循环实现可以替代。可使用循环替代,最关键是每次记录两个值:上个值和上上个值,然后每迭代一次将这两个值更新,如下fibonacci2(int):
public static int fibonacci2(int n){
if (n < 0) {
throw new IllegalArgumentException("The number should be not less than 0");
}
if (n < 2) {
return n;
}
int f0 = 0;
int f1 = 1;
for(int i = 2;i<=n;i++){
int f = f0 + f1;
f0 = f1;
f1 = f;
}
return f1;
}
可以使用以下测试示例比较 fibonacci1(int) 和 fibonacci2(int) 的效率:
for(int i = 0;i< 47;i++){
System.out.println(fibonacci1(i));
//System.out.println(fibonacci2(i));
}
一开始的时候,两者都表现的非常快,但是到后来 fibonacci1(int) 出结果的速度越来越慢,而 fibonacci2(int) 的表现还是一如即往。
使用预定值
如果某些值是频繁读取的,则可以预先把结果存到,例如某个数组里,以后就从数组里读值了。例如:
/**
* 构造从 0 - n 对应的 fibonacci 值的数组
*/
public static int[] fibonacci(int n) {
int[] fArray = new int[n+1];
fArray[0] = 0;
fArray[1] = 1;
int length = fArray.length;
for (int i = 2; i < length; i++) {
fArray[i] = fArray[i - 1] + fArray[i - 2];
}
return fArray;
}
这里根据输入的值 n 构造了每个值对应的 fibonacci 值。以后要想取某个值对应的数列,只要从返回的数组里取即可:
int[] fibonacci = fibonacci(46);
for (int i = 0; i < 47; i++) {
System.out.println(fibonacci[i]);
}
结果的正确
上面说了,当计算到 47 的 fibonacci 值时,结果已经超出了 java 的 int 表示的范围。 方法1就是把int换成long,例如:
public static long fibonacci2(int n){
if (n < 0) {
throw new IllegalArgumentException("The number should be not less than 0");
}
if (n < 2) {
return n;
}
long f0 = 0;
long f1 = 1;
for(int i = 2;i<=n;i++){
long f = f0 + f1;
f0 = f1;
f1 = f;
}
return f1;
}
这次表现好一些,到93才开始出问题。
要保证方法本身的正确性,有两个选择:要么限制可计算的数字的上限;要不扩展类型,让结果可以无限制的大。
限制参数
如果结果是int型,则上限是46:
public static int fibonacci2(int n){
if (n < 0 || n > 46 ) {
throw new IllegalArgumentException("The number should be between 0-46");
}
if (n < 2) {
return n;
}
int f0 = 0;
int f1 = 1;
for(int i = 2;i<=n;i++){
int f = f0 + f1;
f0 = f1;
f1 = f;
}
return f1;
}
如果结果是long型,则上限是92:
public static long fibonacci2(int n){
if (n < 0 || n > 92) {
throw new IllegalArgumentException("The number should be between 0-92");
}
if (n < 2) {
return n;
}
long f0 = 0;
long f1 = 1;
for(int i = 2;i<=n;i++){
long f = f0 + f1;
f0 = f1;
f1 = f;
}
return f1;
}
无上限结果
可以使用BigInteger来产生无限的整数,以避免int或long的上限的问题:
public static BigInteger fibonacci3(int n){
if (n < 0) {
throw new IllegalArgumentException("The number should be a positive number");
}
if (n < 2) {
return BigInteger.valueOf(n);
}
BigInteger f0 = BigInteger.valueOf(0);
BigInteger f1 = BigInteger.valueOf(1);
for(int i = 2;i<=n;i++){
BigInteger f = f0.add(f1);
f0 = f1;
f1 = f;
}
return f1;
}
为了提交效率,可能可以使用分段计算的方式:当n<47时使用基于int的计算方法;当n<93时可以使用基于long的计算方法;否则使用基于BigInteger的计算方法。
相关推荐
利用数组计算斐波那契数列
它允许开发者在后台线程执行耗时操作(如计算斐波那契数列),然后通过Message对象将结果回调到主线程更新UI。在这个场景中,我们讨论的是如何在Android的子线程中计算斐波那契数列,并利用Handler将结果传递回主线...
在这个MATLAB程序中,我们首先定义了斐波那契数列的前两项`fibonacci = [1, 2]`,然后通过`for`循环从第三项开始计算,直到第一百项。在每次循环中,我们使用`fibonacci(k)`存储当前项,它是前两项`fibonacci(k-1)`...
在本文中,我们将深入探讨如何使用汇编语言来计算斐波那契数列的前22项,并且对比两种不同的实现方法:递归调用和普通循环加法。首先,让我们了解一下斐波那契数列的基本概念。 斐波那契数列是一个数学上的序列,...
本实验通过创建多个线程,分别用于执行矩阵乘法和计算斐波那契数列,展示了多线程在并发处理不同计算任务中的应用。 一、线程基础 线程是操作系统分配CPU时间的基本单元,一个进程中可以包含一个或多个线程。与进程...
在C#中,我们可以使用多种方法来计算斐波那契数列。下面我们将探讨几种常见的实现方式: 1. **递归方法**: 递归是最直观的方法,但效率较低,因为它会重复计算许多相同的值。C#代码如下: ```csharp public ...
计算fibonacci数列每一项时所需的递归调用次数
在编程中,特别是C++中,有多种方法可以实现计算斐波那契数列的第n项。非递归的函数调用方式通常比递归更有效率,因为它避免了重复计算和堆栈调用的开销。以下是一种使用循环的非递归方法: ```cpp #include using...
此外,对于更大的n值,还可以使用矩阵快速幂等高级算法来进一步优化斐波那契数列的计算,这种方法适用于需要计算大斐波那契数的情况。 在VC 6.0环境下,你可以直接编译并运行这些C语言源代码。首先,打开Visual C++...
本文档提供了两种计算斐波那契的C++代码函数,供网友使用,。主要讲cpp添加到新建项目中运行
2. **计算Fibonacci数列**: - 使用一个`for`循环来计算从第3项到第20项的值。每次迭代时,`f[i]`的值都等于`f[i-2]`和`f[i-1]`的和。 3. **输出结果**: - 另一个`for`循环用于输出计算得到的Fibonacci数列。...
在本文中,我们将详细探讨四种不同的C++实现方法——递归法、迭代法、矩阵法和公式法,并分析它们在计算斐波那契数列时的时间效率。 首先,递归法是最直观的实现方式,其代码简洁易懂。但是,由于递归调用的开销,...
### 计算斐波那契数列及其递归调用次数 #### 引言 斐波那契数列是一个在数学、计算机科学以及许多其他领域中广泛应用的经典序列。该序列的定义简单直观:序列中的每一项是前两项的和。在计算机科学中,通过递归...
labview通过移位寄存器计算斐波那契数列的第n项
在Java中,我们可以使用两种主要方法来计算斐波那契数列的第n项:递归和循环。 1. **递归解法**: 递归方法基于斐波那契数列的定义,通过函数调用自身来实现。如代码所示,当n小于等于1时,直接返回n,因为这是数列...
根据给定文件的信息,我们可以详细地探讨如何使用Java来实现Fibonacci数列,并通过具体的代码示例来深入了解这一主题。 ### Java实现Fibonacci数列 #### 1. Fibonacci数列简介 Fibonacci数列是一系列数字,其中每...
换句话说,斐波那契数列的第n项(记作F(n))可以通过F(n-1)和F(n-2)来计算。数列的前几项是:0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ...,以此类推。 斐波那契数列在计算机编程中的应用主要体现在以下几个方面: 1. **...
基于JavaScript计算斐波那契数列!!!!
# 题目:斐波那契数列。 # 程序分析:斐波那契数列(Fibonacci sequence),从1,1开始,后面每一项等于前面两项之和。图方便就递归实现,图性能就用循环。
在Java编程中,我们可以使用两种主要方法来计算斐波那契数列的第n个数字:递归和循环。 **递归方法** 是基于函数自身调用来解决问题的方式。在Java中,我们创建了一个名为`fibonacciRecursion`的方法,它接受一个...