zoukankan      html  css  js  c++  java
  • 尾递归优化

    尾递归优化是利用上面的第一个特点“调用同一个方法”来进行优化的 
    尾递归优化其实包括两个东西:1)尾递归的形式;2)编译器对尾递归的优化 
    尾递归的形式 
    尾递归其实只是一种对递归的特殊写法,这种写法原本并不会带来跟递归不一样的影响,它只是写法不一样而已,写成这样不会有任何优化效果,该爆的栈和帧都还会爆 
    具体不一样在哪里 
    前面说了,递归的本质是某个方法调用了自身,尾递归这种形式就要求:某个方法调用自身这件事,一定是该方法做的最后一件事(所以当有需要返回值的时候会是return f(n),没有返回的话就直接是f(n)了) 
    要求很简单,就一条,但是有一些常见的误区 
    这个f(n)外不能加其他东西,因为这就不是最后一件事了,值返回来后还要再干点其他的活,变量空间还需要保留 
    比如如果有返回值的,你不能:乘个常数 return 3f(n);乘个n return n*f(n);甚至是 f(n)+f(n-1) 
    另外,使用return的尾递归还跟函数式编程有一点关系 
    编译器对尾递归的优化 
    上面说了,你光手动写成尾递归的形式,并没有什么卵用,要实现优化,还需要编译器中加入了对尾递归优化的机制 
    有了这个机制,编译的时候,就会自动利用上面的特点一来进行优化 
    具体是怎么优化的: 
    简单说就是重复利用同一个栈帧,不仅不用释放上一个,连下一个新的都不用开,效率非常高(有人做实验,这个比递推比迭代都要效率高) 
    为什么写成尾递归的形式,编译器就能优化了?或者说【编译器对尾递归的优化】的一些深层思想 
    说是深层思想,其实也是因为正好编译器其实在这里没做什么复杂的事,所以很简单 
    由于这两方面的原因,尾递归优化得以实现,而且效果很好 
    因为在递归调用自身的时候,这一层函数已经没有要做的事情了,虽然被递归调用的函数是在当前的函数里,但是他们之间的关系已经在传参的时候了断了,也就是这一层函数的所有变量什么的都不会再被用到了,所以当前函数虽然没有执行完,不能弹出栈,但它确实已经可以出栈了,这是一方面 
    另一方面,正因为调用的是自身,所以需要的存储空间是一毛一样的,那干脆重新刷新这些空间给下一层利用就好了,不用销毁再另开空间 
    有人对写成尾递归形式的说法是【为了告诉编译器这块要尾递归】,这种说法可能会导致误解,因为不是只告诉编译器就行,而是你需要做优化的前半部分,之后编译器做后半部分 
    所以总结:为了解决递归的开销大问题,使用尾递归优化,具体分两步:1)你把递归调用的形式写成尾递归的形式;2)编译器碰到尾递归,自动按照某种特定的方式进行优化编译

    https://blog.csdn.net/yan_chou/article/details/59488871

    斐波那契数列

     常规的斐波那契数列算法可能是这样的:

    1
    2
    3
    4
    5
    6
    7
    int fib(int n) {
     
        if (n <= 2) {
            return 1;
        }
        return fib(n - 1) + fib(n - 2);
    }

      

    上面的这种递归计算最终的return操作是加法操作。所以不是尾递归。

    如果用尾递归就是这样的:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    /**
     计算第n位斐波那契数列的值
      
     @param n 第n个数
     @param acc1 第n个数
     @param acc2 第n与第n+1个数的和
     @return 返回斐波那契数列值
     */
    int tailfib(int n,int acc1,int acc2) {
        if (n < 2) {
            return acc1;
        }
         
        return tailfib(n-1,acc2,acc1 + acc2); 
    }

      

    比如我们想计算第10位斐波那契数列的值,只需要fib(10,1,1)即可。

    看一下测试效果,测试程序如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    int main(int argc, const char * argv[]) {
        clock_t start,finish;
        
        start = clock();
        printf("计算结果:%d ", fib(45));
        finish = clock();
        printf("花费时间--------%lu ",finish - start);
     
         
        start = clock();
        printf("计算结果:%d ", tailfib(45,1,1));
        finish = clock();
         
        printf("花费时间--------%lu ",finish - start);
        return 0;
         
    }

      

    计算结果如下:

    1
    2
    3
    4
    5
    计算结果:1134903170
    花费时间--------5540692
    计算结果:1134903170
    花费时间--------4
    Program ended with exit code: 0

      https://www.cnblogs.com/zhanggui/p/7722541.html

  • 相关阅读:
    Java异常面试题
    Quickhit快速击键
    多态and接口
    Java面向对象编程概述
    学生管理系统--分层开发
    类型转换
    文件上传
    ongl(示例3-6 多值类型的数据处理)
    ongl(原始类型和包装类型)
    Interceptor
  • 原文地址:https://www.cnblogs.com/feng9exe/p/9897923.html
Copyright © 2011-2022 走看看