前一篇文章讨论了Fibonacci的3种实现方式。本文对Fibonacci 3种实现方式的性能进行了测试。
本次实验是使用3种实现方式来求出Fibonacci的第n个值,观察他们的执行时间来分析其执行效率。
方式 | 参数 n 的值 | 执行时间 |
递归 | 48 | 25380ms |
循环 | 48 | 0ms |
stack | 48 | 0ms |
递归 | 100 | 时间过长,>60s |
循环 | 100 | 0ms |
stack | 100 | 0ms |
递归 | 100 0000 | 时间过长,>60s |
循环 | 100 0000 | 4ms |
stack | 100 0000 | 114ms |
由测试结果可以看出,递归算法的时间效率远远低于非递归算法。那么时间去哪了呢?
笔者又做了一组测试,观察递归算法来实现Fibonacci的第n个数的值时递归方法调用的次数,和递归算法实现阶乘Factorial的第n个数值时方法调用的次数。
参数n的值 | 方法调用的次数 | F(n)的值 | |
阶乘 | 3 | 3 | 6 |
Fibonacci | 3 | 3 | 1 |
阶乘 | 5 | 5 | 120 |
Fibonacci | 5 | 9 | 3 |
阶乘 | 10 | 10 | 3628800 |
Fibonacci | 10 | 109 | 34 |
由上述的测试结果可看出,同样是求第n个数的值,Fibonacci的调用方法的次数明显多于n的阶乘的次数,增长趋势要增长的更加明显。导致这样的原因是什么呢?
分析一下源码的实现。
Fibonacci 的实现:
public int Fib_Recursion(int n){
/*System.out.println("----------"+n);*/
count_Fib++;
if(n<=0)
return 0;
if(n==1){
return 0;
}
if(n==2){
return 1;
}
return Fib_Recursion(n-1)+Fib_Recursion(n-2);
}
阶乘的实现:
public int factorial(int n){
count_factorial++;
if(n==1){
return 1;
}
return n*factorial(n-1);
}
通过源码可以看出,Fibonacci的源码中调用方法逻辑为 Fib_Recursion(n-1)+Fib_Recursion(n-2) 正式因为这个逻辑,使得当求第n个值的时候,调用了两次自身的方法。
而阶乘的递归调用逻辑为n*factorial(n-1)
,也就是说当求第n个值的时候,调用了一次自身的方法。所以随着n的增加。阶乘递归调用方法的次数是线性的,也就是说方法的调用次数跟跟n的关系为O(n), Fibonacci方法调用的次数跟n的关系是什么样的呢?2的0次方+2的1次方+...2的n-1次方,这样的一个数量级。按照等比数列求和,调用次数的数量级约为O(2的n-2次方)。所以这就是为什么Fibonacci的效率如此之低。
本文所用的测试源码:
package com.pip.structure;
import java.util.Stack;
public class Fibonacci {
public int count_Fib=0;
public int count_factorial=0;
public static Fibonacci getInstance(){
return new Fibonacci();
}
public int Fib(int n){
Stack<Integer> s=new Stack<Integer>();
int top=0;
if(n==0)
return 0;
while(top<=n){
if(top==0){
s.push(0);
top++;
continue;
}
if(top==1){
s.push(1);
top++;
continue;
}
s.push(s.get(top-1)+s.get(top-2));
top++;
continue;
}
return s.peek();
}
public int Fib_nonRecursion(int n){
int pre_1=0,pre_2=0,temp;
for(int i=1;i<=n;i++){
if(i==1){
pre_2=0;
}
if(i==2){
pre_1=0;
pre_2=1;
}
temp=pre_2;
pre_2=pre_1+pre_2;
pre_1=temp;
}
return pre_2;
}
public int Fib_Recursion(int n){
/*System.out.println("----------"+n);*/
count_Fib++;
if(n<=0)
return 0;
if(n==1){
return 0;
}
if(n==2){
return 1;
}
return Fib_Recursion(n-1)+Fib_Recursion(n-2);
}
public int factorial(int n){
count_factorial++;
if(n==1){
return 1;
}
return n*factorial(n-1);
}
}
package com.pip.structure;
import java.util.Date;
import org.junit.Test;
public class TestFib {
@Test
public void compareFibRecursion() {
// test recursion
Fibonacci f=Fibonacci.getInstance();
long beforeTime = new Date().getTime();
f.Fib_Recursion(48);
long afterTime = new Date().getTime();
System.out.println("recursion-time--" + (afterTime - beforeTime) + "ms");
// test non recursion
long beforeTime_non = new Date().getTime();
Fibonacci.getInstance().Fib_nonRecursion(48);
long afterTime_non = new Date().getTime();
System.out.println("non_recursion-time--"+ (afterTime_non - beforeTime_non) + "ms");
// test stack
long beforeTime_stack = new Date().getTime();
Fibonacci.getInstance().Fib(48);
long afterTime_stack = new Date().getTime();
System.out.println("stack-time--"+ (afterTime_stack - beforeTime_stack) + "ms");
}
@Test
public void compareFibRecursion_100() {
// test recursion
Fibonacci f=Fibonacci.getInstance();
long beforeTime = new Date().getTime();
f.Fib_Recursion(100);
long afterTime = new Date().getTime();
System.out.println("recursion-time--" + (afterTime - beforeTime) + "ms");
// test non recursion
long beforeTime_non = new Date().getTime();
Fibonacci.getInstance().Fib_nonRecursion(100);
long afterTime_non = new Date().getTime();
System.out.println("non_recursion-time--"+ (afterTime_non - beforeTime_non) + "ms");
// test stack
long beforeTime_stack = new Date().getTime();
Fibonacci.getInstance().Fib(100);
long afterTime_stack = new Date().getTime();
System.out.println("stack-time--"+ (afterTime_stack - beforeTime_stack) + "ms");
}
@Test
public void compareFibRecursion_10000() {
// test recursion
Fibonacci f=Fibonacci.getInstance();
long beforeTime = new Date().getTime();
f.Fib_Recursion(10000);
long afterTime = new Date().getTime();
System.out.println("recursion-time--" + (afterTime - beforeTime) + "ms");
// test non recursion
long beforeTime_non = new Date().getTime();
Fibonacci.getInstance().Fib_nonRecursion(10000);
long afterTime_non = new Date().getTime();
System.out.println("non_recursion-time--"+ (afterTime_non - beforeTime_non) + "ms");
// test stack
long beforeTime_stack = new Date().getTime();
Fibonacci.getInstance().Fib(10000);
long afterTime_stack = new Date().getTime();
System.out.println("stack-time--"+ (afterTime_stack - beforeTime_stack) + "ms");
}
@Test
public void compareFibRecursion_1000000() {
// test recursion
Fibonacci f=Fibonacci.getInstance();
long beforeTime = new Date().getTime();
f.Fib_Recursion(1000000);
long afterTime = new Date().getTime();
System.out.println("recursion-time--" + (afterTime - beforeTime) + "ms");
// test non recursion
long beforeTime_non = new Date().getTime();
Fibonacci.getInstance().Fib_nonRecursion(1000000);
long afterTime_non = new Date().getTime();
System.out.println("non_recursion-time--"+ (afterTime_non - beforeTime_non) + "ms");
// test stack
long beforeTime_stack = new Date().getTime();
Fibonacci.getInstance().Fib(1000000);
long afterTime_stack = new Date().getTime();
System.out.println("stack-time--"+ (afterTime_stack - beforeTime_stack) + "ms");
}
@Test
public void compareFibAndFac() {
Fibonacci f = Fibonacci.getInstance();
System.out.println("Factorial value-----" +f.factorial(3));
System.out.println("Factorial count-----" + f.count_factorial);
System.out.println("Fibonacci value-----" + f.Fib_Recursion(3));
System.out.println("Fibonacci count-----" + f.count_Fib);
Fibonacci f2 = Fibonacci.getInstance();
System.out.println("Factorial value-----" + f2.factorial(5));
System.out.println("Factorial count-----" + f2.count_factorial);
System.out.println("Fibonacci value-----" +f2.Fib_Recursion(5));
System.out.println("Fibonacci count-----" + f2.count_Fib);
Fibonacci f3 = Fibonacci.getInstance();
System.out.println("Factorial value-----" + f3.factorial(10));
System.out.println("Factorial count-----" + f3.count_factorial);
System.out.println("Fibonacci value-----" +f3.Fib_Recursion(10));
System.out.println("Fibonacci count-----" + f3.count_Fib);
}
}