C++ 使显式堆栈算法更快
有一些递归算法可以很快填满堆栈。一种解决方案是使堆栈显式,从而将算法转化为迭代算法 但我注意到,显式堆栈会使算法变得相当慢(这可能不应该让我感到惊讶)。有什么通用的C++准则让显式栈更快吗?它们是否可能比最初的递归算法运行得更快C++ 使显式堆栈算法更快,c++,recursion,C++,Recursion,有一些递归算法可以很快填满堆栈。一种解决方案是使堆栈显式,从而将算法转化为迭代算法 但我注意到,显式堆栈会使算法变得相当慢(这可能不应该让我感到惊讶)。有什么通用的C++准则让显式栈更快吗?它们是否可能比最初的递归算法运行得更快 编辑:下面是我为其编写的显式堆栈的函数。我还粘贴了迭代代码。出于某种原因,使用std::vector比使用std::stack更快,这非常令人惊讶 // A(m, n) = n + 1 if m = 0 // = A(m
编辑:下面是我为其编写的显式堆栈的函数。我还粘贴了迭代代码。出于某种原因,使用
std::vector
比使用std::stack
更快,这非常令人惊讶
// A(m, n) = n + 1 if m = 0
// = A(m - 1, 1) if m > 0 and n = 0
// = A(m - 1, A(m, n - 1)) if m, n > 0
int A(int m, int n, long& iterations,
std::vector<std::pair<int, int> >& stack)
{
stack.push_back(std::make_pair(m, n));
long result = 0;
bool result_available = false;
while (stack.size() > 0)
{
iterations += 1;
if (result_available) {
stack.back().second = result;
result_available = false;
}
m = stack.back().first;
n = stack.back().second;
stack.pop_back();
if (m == 0) {
result = n + 1;
result_available = true;
}
else if (m > 0 && n == 0) {
stack.push_back(std::make_pair(m - 1, 1));
}
else if (m > 0 && n > 0) {
stack.push_back(std::make_pair(m - 1, n));
stack.push_back(std::make_pair(m, n - 1));
}
}
return result;
}
//A(m,n)=n+1如果m=0
//=A(m-1,1),如果m>0且n=0
//=A(m-1,A(m,n-1))如果m,n>0
int A(int m,int n,long&iterations,
标准:向量和堆栈)
{
stack.push_back(std::make_pair(m,n));
长结果=0;
bool result_available=false;
while(stack.size()>0)
{
迭代次数+=1;
如果(结果可用){
stack.back().second=结果;
结果_可用=错误;
}
m=堆栈。返回()。首先;
n=stack.back().秒;
stack.pop_back();
如果(m==0){
结果=n+1;
结果_可用=真;
}
else如果(m>0&&n==0){
stack.push_-back(std::make_-pair(m-1,1));
}
else如果(m>0&&n>0){
stack.push_-back(std::make_-pair(m-1,n));
stack.push_-back(std::make_-pair(m,n-1));
}
}
返回结果;
}
对于gcc最新版本的幸运用户,还有另一个解决方案:-fsplit stack
拆分堆栈不是一个新想法,Lisp当时就有这个想法。。。这仅仅意味着编译器创建的程序不必预先设置完整的堆栈,而是能够根据需要对其进行扩展。因此,堆栈变得不连续
当然,这需要所有(或大多数)库都适应这种新的堆栈机制(因此需要重新编译整个软件堆栈)。忽略它的库可能仍然会创建堆栈溢出异常,如果您避免在此类库中使用深度递归函数,那么这并不是什么问题
使用这种机制,只要您有可用内存,堆栈就会增长以容纳您的程序
如果您包括一个递归和显式堆栈版本的算法的示例,在该算法中,计时差别很大,那么回答这个问题就会容易得多。一般来说,很难回答性能问题(除了算法复杂性),因为影响性能的因素太多。严格地猜测(正如@Mankarse所说,显示一些代码),但创建堆栈帧通常比分配堆内存快得多,因此如果迭代方法最终频繁扩展堆栈,堆分配会对性能产生影响。解决方案是减少分配的频率…某些类型的递归可以消除为循环(尾部递归)。那么你根本不需要这个堆栈。了解实际的算法和递归行为会很好。@Mankarse我添加了原始函数和代码。对于Fibonacci,我们不要忘记第一个改进是使用O(N)实现(感谢记忆化/动态编程)是的-这是第二个带尾部递归的递归算法。但是如果你消除了算法中的尾部递归,它会更快。