C#:递归方法期间的堆栈溢出异常

问题描述

我有一个递归方法,每当随机生成的数字不等于1时,该方法便会自行调用。 我正在尝试测试不同事物的几率,例如minecraft中的神奇宝贝(1/8192)或12眼种子(10 ^ 12),即使我了解堆栈溢出的原因,我也不知道如何解决。使用“线程”会使速度降低很多(不使用线程时每秒可进行5000次计算,使用线程时大约为500次)。

这是代码

static void shiny()
{
    total = 0;
    counter += 1;
    resetcounter += 1;
    if (rdm.Next(8192) + 1 == 1)
    {
        Console.WriteLine("SHINY !! In: " + counter + " resets.");
    }
    else
    {
        if (resetcounter > 7000)
        {
            Console.WriteLine("Reset. Current: " + counter);
            ThreadStart newtask = new ThreadStart(shiny);
            Thread task = new Thread(newtask);
            task.Start();
        }
        else
        {
            Console.WriteLine("Reset. Current: " + counter);
            shiny();
        }
    }
}

我使用resetcounter变量来避免堆栈溢出错误,因为它发生在7k“复位”周围,然后它启动了一个新线程。 我很想了解测试赔率如何可以避免堆栈溢出!

解决方法

了解一些背景信息。 C#和许多其他语言在调用方法时使用call stack,用于局部变量,返回值和其他内容。因此,在调用方法时,堆栈的大小将增加,而在方法返回时,堆栈的大小将减少相同的数量。最大大小通常为1-4Mb,在使用没有明确定义最大深度的递归代码时,很容易达到。

可以将递归函数重写为迭代函数。在某些情况下,需要显式的stack可以大得多,但在这种情况下则不需要。减去线程的示例代码可以按如下方式重写:

    void shiny()
    {
        while (rdm.Next(8192) != 0)
        {
            counter += 1;
        }
        Console.WriteLine("SHINY !! In: " + counter + " resets.");
    }

尽管这样的实验很有趣,但是您可以显式地计算概率。假设在一轮中发现有光泽的机会是8192中的一,即0.012%,则在n轮后发现至少有光泽的机会为1-(8191/8192)^ n。将其扔进Wolfram alpha中,您会得到probability plot