递归堆栈大小?

时间:2011-09-24 23:18:17

标签: c# recursion overflowexception

class Program  
{
    static void Main(string[] args)
    {
        Test(0);
    }

    static void Test(int i)
    {
        if (i > 30000)
        {
            return;
        }
        Test(i + 1);
    }
}

为什么在调用上面的示例时获取递归函数并抛出StackOverflowException。

(因为超过默认的递归堆栈大小?)

但我想知道如何解决这个问题。

感谢。

5 个答案:

答案 0 :(得分:5)

问题是你做了太多的递归。无论你在做什么,都会使用如此多的递归级别,应该使用循环来解决。

与递归一起工作的算法不会对每个项使用一级递归。通常,您会将每个级别的工作分成两半,因此30000项只需要15级递归。

答案 1 :(得分:5)

你得到一个例外,因为30,000个堆栈帧是一个相当大的数字: - )

您通过以更谨慎的方式使用递归来解决它。递归地解决的理想问题是那些迅速减少“搜索空间”(a)的问题。

例如,二进制树遍历每次重复时搜索空间减半:

def find (searchkey, node):
    if node = NULL:
        return NULL
    if searchkey = node.key:
        return node
    if searchkey < node.key:
        return find (searchkey, node.left)
    return find (searchkey, node.right)

添加两个无符号整数(以及上面你自己的算法)非常适合递归,因为在计算结果之前很久就会耗尽堆栈分配。:

def add (a, b):
    if a = 0:
        return b
    return add (a-1, b+1)

(a)搜索空间可以定义为整套可能的答案。你想尽快减少它。

而且,顺便说一句,递归的理想问题与理论/数学意义上的堆栈空间无关,它们只是可以表达为的任何问题:

  • 与“更简单”的论点相同或类似的问题。
  • 带有“最简单”参数的终止条件。

(在这个意义上,“简单”意味着接近终止条件。)

理论/数学方法不需要考虑堆栈空间,但我们作为计算机科学家必须这样做。现实设定限制: - )

另请参阅When would I not use recursion?Situations where you would convert recursion to iteration

答案 2 :(得分:2)

您收到StackOverflowException,因为您的堆栈在对Test的30k次调用中溢出。没有办法“解决”问题,堆栈大小有限(并且也很小)。您可以重新设计代码以便以迭代方式工作。

for( int i = 0; i <= 30000; ++i )
{
    Test( i );
}

但是,我认为您的实际用例比这更复杂;否则递归没有收获。

答案 3 :(得分:2)

虽然你可以手动指定新线程的堆栈大小,但我会使用Stack<T>或循环(这就是你的简化示例所需要的所有内容)所以你不要根本不需要递归,即:

Stack<int> stack = new Stack<int>();
stack.Push(1);
while(stack.Count > 0)
{
    int someValue = stack.Pop();
    //some calculation based on someValue
    if(someValue < 30000)
        stack.Push(someValue +1);
}

答案 4 :(得分:1)

看看这个问题:Way to go from recursion to iteration

如果您的递归将达到30k +级别深度......您可能希望将其转换为迭代算法。该问题的答案应该有所帮助。