java实现斐波那契函数的3种方式比较,以及是否存在尾调用问题

使用java实现斐波那契数列的3中方式

public class Demo {

	public static void main(String[] args) {
	    // num = 10 、20 、50 时 测试斐波那契函数耗时和计算值
	    testFibonacci(10);
	    System.out.println("--------------");
	    testFibonacci(20);
	    System.out.println("--------------");
	    testFibonacci(50);
	    System.out.println("--------------");
	    
	}
	
	public static void testFibonacci(int num){
	    // 第50 个 斐波那契数
        System.out.println("num = "+ num);
        long start = System.nanoTime();
        long val = Fibonacci(num);
        long end = System.nanoTime();
        System.out.println("Fibonacci 耗时:" +(end - start)+",值="+val);
        
        start = System.nanoTime();
        val = Fibonacci2(num,1,1);
        end = System.nanoTime();
        System.out.println("Fibonacci2 耗时:" +(end - start)+",值="+val);
        
        start = System.nanoTime();
        val = Fibonacci3(num);
        end = System.nanoTime();
        System.out.println("Fibonacci3 耗时:" +(end - start)+",值="+val);
	}
	
	/** 斐波那契 非尾调用*/
	public static long Fibonacci(long n){
	    if ( n <= 1 ) {return 1;}
	    return Fibonacci(n - 1) + Fibonacci(n - 2);
	}
	
	/** 斐波那契 尾调用*/
	public static long Fibonacci2(int n,long num1 ,long num2 ){
        if ( n <= 1 ) {return num2;}
        return Fibonacci2 (n - 1, num2, num1 + num2);
    }
	
	/** 斐波那契 for*/
	public static long Fibonacci3(int n){
        if ( n <= 1 ) {return 1;}
        long num1 = 1;
        long num2 = 1;
        long val = 0;
        for (int i = 2; i <= n; i++) {
            val = num1+num2;
            num1 = num2;
            num2 = val;
        }
        return val;
    }
}

执行main函数,控制台打印的信息来看,随着num的数值越大,非尾调用方式执行的时间越长,比对另外2种方式时间差距巨大,而尾调用和for循环执行的时间相差不大。所以java函数尾递归调用是明显存在的。

 

尾递归

函数调用自身,称为递归。如果尾调用自身,就称为尾递归。

递归非常耗费内存,因为需要同时保存成千上百个调用帧,很容易发生“栈溢出”错误(stack overflow)。但对于尾递归来说,由于只存在一个调用帧,所以永远不会发生“栈溢出”错误。

 

尾调用优化

尾调用之所以与其他调用不同,就在于它的特殊的调用位置。

我们知道,函数调用会在内存形成一个“调用记录”,又称“调用帧”(call frame),保存调用位置和内部变量等信息。如果在函数A的内部调用函数B,那么在A的调用帧上方,还会形成一个B的调用帧。等到B运行结束,将结果返回到AB的调用帧才会消失。如果函数B内部还调用函数C,那就还有一个C的调用帧,以此类推。所有的调用帧,就形成一个“调用栈”(call stack)。

尾调用由于是函数的最后一步操作,所以不需要保留外层函数的调用帧,因为调用位置、内部变量等信息都不会再用到了,只要直接用内层函数的调用帧,取代外层函数的调用帧就可以了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值