将公共递归转换为尾递归,因为大型输入的堆栈溢出
converting a common recursion to tail recurtion because of stack overflow for large inputs
我发现这个算法用于一个程序,该程序返回具有特定周长的可能三角形的数量。我知道这个问题还有其他一些方法,但使用它们,我得到大数字的时间限制错误,可能是因为它们没有优化。这个似乎更优化,但我得到了堆栈溢出以进行大输入的递归。任何人都可以通过将其转换为尾递归或对此问题做其他事情来帮助我优化它吗?
这是我找到的算法:
#include<iostream>
using namespace std;
int foo(int);
int main(){
int n;
cin>>n;
cout<<foo(n);
return 0;
}
int foo(int n){
int p=n/2-2;
int t;
if(p%6<5&&p%6>0)
t=(p+6-p%6)/6;
else if(p%6==5)
t=(p+6-p%6)/6+1;
else
t=p/6;
if(n==3||n==5||n==6)
return 1;
else if(n==4)
return 0;
else{
if(n%2==0)
return foo(n-3);
else
return foo(n+1)+t;
}
}
问题就在这里:
return foo(n+1) + t;
// ^^^
加法使得递归调用foo
不是函数中发生的最后一件事。因此,您需要将该添加项移动到函数中:
return foo(n + 1, t);
为了使另一个递归调用兼容,您只需提供 add 的中性元素:
return foo(n-3, 0);
新函数的签名:
int foo(int n, int offset = 0); // default parameter allows for calling just as before.
剩下的最后一个问题:在哪里添加???
嗯,有两个地方很明显:
if(n==3||n==5||n==6)
return 1 + offset;
else if(n==4)
return /*0 +*/ offset;
offset
是所有先前递归调用的累积偏移量!因此,您必须将其添加到添加到下一个函数的递归调用的任何内容中,因此:
// ...
else if(n % 2 == 0)
return foo(n-3, /*0 +*/ offset);
else
return foo(n+1, t + offset);
旁注:您的算法似乎不适合负数。使用这些调用(未修改的(foo
最终会重复出现,直到堆栈溢出(如果堆栈足够大,则由于有符号整数溢出,可能会导致未定义的行为(。如果您不打算在负数上使用它,则绝对应该使用unsigned int
作为数据类型!否则,它需要适当的修复。
值 0、1 和 2 也需要特殊处理,它们会导致负n
(对于未修改的foo
,见上文(。但你几乎可以轻而易举地做到这一点:
if(n == 4)
{ ... }
else if (n < 6)
{ ... }
这将返回 1 对于 n 为 0、1 或 2(即使使用尾部调用优化版本,因为偏移量仍然是 0(,甚至省去一些比较。如果需要为特殊值返回 0:也很简单:
else if (n < 6)
return (n >= 3) + offset;
您甚至可以将所有内容合并为一个条件:
if(n < 6)
return (n == 3 || n > 4) + offset; // returning 0 for 0, 1, 2
return (n <= 3 || n > 4) + offset; // returning 1 for 0, 1, 2
最大的优势:在所有这些递归期间,您只需要一次检查,而所有其他检查仅在满足停止条件时才执行(即仅一次(。
您可以将p
和t
的计算移动到停止条件后面,无论如何您都不会在那里使用它们......
- 删除一个线程上有数百万个字符串的大型哈希映射会影响另一个线程的性能
- C++中高效的大型稀疏块压缩线性方程
- C++ Windows 驱动程序MSB3030无法复制该文件,因为它找不到
- 如何实现高效的算法来计算大型数据集的多个不同值?
- 我可以在这里替换什么,因为我不能在 C# 中使用隐式变量的 lambda 函数?
- LMDB:在有限的内存系统中打开大型数据库
- 程序无法启动,因为缺少 libmpc-3.dll
- 如何在大型c++项目的可视化代码中设置调试
- 仅在大型阵列上出现合并排序分段错误
- 数组类型 int[n][n] 不可赋值,因为表达式必须具有常量值
- 在这个函数中是有缺陷的,因为取消引用 null 是无效的,所以我想更改代码
- 无法为 X.radio 创建中等可视化,因为找不到网络节点可视化 - Omnet++
- 在编译时,C++项目抛出错误 C2228,这是预期的,因为控件在运行时未达到该点
- C++ 程序不会因为内存而终止
- 在堆栈上C++大型多维数组
- atoi() 在应用于大型命令行参数时会产生不正确的值
- 为什么"fun(i)"被推导出为"fun<int&>"而不是"fun<int>",因为"i"是"int"的类型而不是参考?
- 如何在 c++ 中通过 http 发送大型视频文件?
- 将公共递归转换为尾递归,因为大型输入的堆栈溢出
- 无法使用 openssl 服务器,因为密码已过时...什么的...ERR_SSL_VERSION_OR_CIPHER_MISMATCH