科普一下:
(Fibonacci)斐波那契数列,又称黄金分割数列,指的是这样一个数列:0、1、1、2、3、5、8、13、21、……在数学上,斐波纳契数列以如下被以递归的方法定义:F(0)=0,F(1)=1,F(n)=F(n-1)+F(n-2)(n≥2,n∈N*
现在输入n(n > 1),求第n位斐波那契数。
代码1:简单实现
public static long computeRecursively(int n){
if(n > 1)
return computeRecursively(n-2)+computeRecursively(n-1);
else
return n;
}
分析:计算n=30时,斐波那契数用了2692537次调用,这里的大数量的递归将会是我们关注的效率瓶颈。我们发现,每个斐波数(少于2除外),都需要开启两个递归方法来实现
因此,我们可以优化为一个递归来实现。如下:
代码2:优化递归
public static long computeRecursivelyWithLoop(int n){
if(n > 1){
long result = 1;
do{
result += computeRecursivelyWithLoop(n-2);
n--;
}while(n > 1);
return result;
}else
return n;
}
分析,这里的递归,计算n=30时,产生的递归调用有1346269次,对比上面的计算方法,效率快了一倍,但是依然是低效率的方法。接下来,我们考虑下非递归(迭代法)
代码3:public static long computeIteratively(int n){
if(n > 1){
long a = 0, b =1;
do{
long tmp = b;
b += a;
a = tmp;
n--;
}while( n > 1);
return b;
}
return n;
}
分析:这里的思路主要是从斐波数的性质出发。可以看出代码有交换的意图,易知b比a大。由于第n项由等于前两项的和,所以,每次计算F(N) = F(N-1)+F(N-2) <=> b = b + a;所以每次只需将b 覆盖 a ,b+a覆盖b, 就可以向后继续计算。因为其是线性的,效率大大提高。计算computeIteratively的次数少于10万次。
最快一些?可以。考虑下,如果n是奇数,则a = 0,b = 1。n是偶数的话,则a = 1 ,b = 1。
代码4:
public static long computeIterattivelyFaster(int n){
if(n > 1){
long a , b = 1;
n--;
a = n & 1;
n /= 2;
while( n > 0){
n --;
a += b;
b += a;
}
return b;
}
return n;
}
这里新版本主要在于每次只迭n /= 2; 每次循环直接计算两个斐波数,所以速度又快了一倍。但是会有个缺陷,JAVA的long是64位,意味着在计算n > 92时会溢出,大家注意一下。