繁体   English   中英

C ++中的Tail Recursion,带有多个递归函数调用

[英]Tail Recursion in C++ with multiple recursive function calls

我正在阅读关于尾递归的这篇文章

我将复制发布的解决方案:

unsigned int f( unsigned int a ) {
   if ( a == 0 ) {
      return a;
   }
   return f( a - 1 );   // tail recursion
}

我想知道,如果结果取决于几个递归函数调用呢? 例如:

unsigned int f( unsigned int a ) {
    if ( a == 0 ) {
          return a;
       }
    return f(a -1) + f( a - 1 );   
}

上面的代码会被编译器优化吗?

目前看,尾递归不适用。 但是如果你看一下你所链接问题的第二个答案的结尾,你就可以看到如何恰当地重写这个功能。 从...开始

unsigned int f( unsigned int a ) {
    if ( a == 0 ) {
          return a;
    }
    return f(a-1) + f(a-1);   
}

重写如下:

unsigned int f( unsigned int a ) {
    if ( a == 0 ) {
          return a;
    }
    return 2 * f(a-1);  
}

即使是现在,尾递归仍然不能直接应用。 我们需要确保返回严格地以return f(....)的形式。 再次重写该功能:

unsigned int f( unsigned int a, unsigned int multiplicative_accumulator = 1 ) {
    if ( a == 0 ) {
          return multiplicative_accumulator * a;
    }
    return f(a-1, multiplicative_accumulator * 2 );   
}

现在,尾递归适用。 这使用multiplicative_accumulator的默认值(感谢@Pubby),以便第一次调用f可以简单地为f(x) ,否则你必须写一些f(x,1)

感谢@SteveJessop做了几个最后的笔记:

  • f(a+1)+f(a+1)更改为2*f(a+1)是安全的,因为f没有副作用(打印,修改堆,这种事情)。 如果f确实有副作用,则重写无效。
  • 原文相当于(2*(2*(2*a)) (或更确切地说, (((a+a)+(a+a))+((a+a)+(a+a))) )而当前的版本更像是(((2*2)*2)*a) 。这很好,特别是对于整数,因为乘法是关联的和分配的。但这对于float来说并不完全等价,其中你可能会得到小的舍入差异。使用浮点运算,有时a*b*c可能与c*b*a略有不同。

第二个函数不是尾递归的,不能用循环轻松替换,所以很可能编译器不会这样做。

这不是尾递归(函数的结果是递归调用的结果):在递归(添加)之后有一个操作要做。 有一个更复杂的转换(取决于加法的交换性)来获得尾递归:使用累加器添加辅助函数:

unsigned int f_helper(unsigned int a, unsigned int acc)
{
   if (a == 0) {
      return acc;
   }
   return f_helper(a-1, f(a-1)+acc);
}

unsigned int f(unsigned int a) {
    if (a == 0) {
          return a;
    }
    return f_helper(a-1, f(a-1));
}

你可以转换成一个循环

unsigned int f_helper(unsigned int a, unsigned int acc)
{
   while (a != 0) {
      acc += f(a-1);
      a = a-1;
   }
   return acc;
}

unsigned int f(unsigned int a) {
    if (a == 0) {
       return a;
    }
    return f_helper(a-1, f(a-1));
}

然后把它放回f

unsigned int f( unsigned int a ) {
  if (a == 0) {
      return a;
   }
   unsigned acc = f(a-1);
   a = a-1;
   while (a != 0) {
      acc += f(a-1);
      a = a-1;
   }
   return acc;
}

我希望它不会像其他人所说的那样进行优化,但通常可以通过使用memoization来解决这些类型的问题,memoization使用内存而不是进行另一次递归调用。

有关使用C ++的一个很好的解释,您可以查看http://marknelson.us/2007/08/01/memoization/

在您的示例中,最后一个呼叫可以替换为

return 2 * f(a - 1);

这将是可以优化的。

在很多情况下,尾部递归似乎不起作用,可能只需要查看算法,并进行一些更改以帮助实现此优化。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM