尾递归优化

Posted feng9exe

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了尾递归优化相关的知识,希望对你有一定的参考价值。

尾递归优化是利用上面的第一个特点“调用同一个方法”来进行优化的 
尾递归优化其实包括两个东西: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






















以上是关于尾递归优化的主要内容,如果未能解决你的问题,请参考以下文章

G++ 尾递归优化失败

尾调用及递归优化

尾递归优化

如何看待以及理解Python的这种尾递归优化

递归的优化

Oz 中的尾递归优化