【问题标题】:Intermittent stack overflow exception when using random numbers使用随机数时出现间歇性堆栈溢出异常
【发布时间】:2012-02-20 02:06:30
【问题描述】:
public int S1x;

public void Execute()
{
    Random random = new Random();
    S1x = random.Next(14, 146);
    if (S1x % 15 != 0)
        Fix(S1x);
}

public int Fix(int SX)
{                
    Random randomG = new Random();
    SX = randomG.Next(14, 146);
    if (SX % 15 != 0)                               
        Fix(SX); // This is the recursion

    return SX;
}

每隔几次运行它都会正常工作,但是我会尝试再次编译并运行它,它会给我这个错误:

System.StackOverflowException 未处理 mscorlib.dll 中出现“System.StackOverflowException”类型的未处理异常 {无法计算表达式,因为当前线程处于堆栈溢出状态。}

而且,是的,我知道有一种更简单的方法可以做到这一点,但是我将值设置为等于 14 到 146 之间的随机数的方法,同时还确保它是 15 的倍数,应该仍然有效尽管如此。

我也很困惑为什么它有时只会给我错误消息。

那么它有什么问题呢?为什么它被标记为无限递归,即使它没有无限的东西?

【问题讨论】:

  • 所以有时和随机不点击你?如果在堆栈空间用完之前没有出现可被 15 整除的数字怎么办?
  • 等等?为什么该代码的其余部分被删除了?

标签: c# random recursion stack-overflow


【解决方案1】:

即使您解决了问题——每次根据当前时间播种时都会创建一个新的 Random 对象——您的代码仍然既不正确也不高效。请记住,递归方法需要具有以下特征才能正确:

  • 一个小问题可以不用递归解决
  • 递归步骤使问题更小
  • 有限数量的递归总是将问题简化为微不足道的问题

您没有任何这些属性,因此递归是错误的解决方案

如果您想要一个介于 14 和 146 之间且也是 15 的倍数的随机数,则不需要任何递归。唯一的数字是 15、30、45、60、75、90、105、120 和 135。所以说:

private int[] array = { 15, 30, 45, 60, 75, 90, 105, 120, 135 };
private Random random = new Random();
...

return array[random.Next(0, array.Length)];

甚至更好:在 1 到 9 之间选择一个随机数,然后乘以 15。

return random.Next(1, 10) * 15;

【讨论】:

  • 谢谢,我实际上最终使用了第二种解决方案,因为这是我的商业/计算机科学老师第一次看到我的问题时建议的。
  • @KevinYoung 我还要提一下,一般来说,如果你有一个递归算法会破坏堆栈(其中递归是 right 解决方案),解决方案是把它变成一个迭代的。在这种情况下,您只需循环 while ((SX % 15) != 0);那么,如果函数碰巧得到一长串数字,其中没有一个是 15 的倍数,那么该函数将需要很长时间。当然,random.Next(1, 10) * 15 是最简单的解决方案。
【解决方案2】:

new Random()Environment.TickCount (系统启动后的毫秒数)播种自身以生成伪随机数,如果你用相同的数字播种两次rand.Next(x, y); 的第一次调用将每次返回相同的值。

    public int Fix(int SX)
    {

        Random randomG = new Random();
        SX = randomG.Next(14, 146);
        if (SX % 15 != 0)
        {

            Fix(SX); // This is the recursion
        }           
        return SX;
    }

由于您每次运行此函数时(在同一毫秒内)都会创建一个 Random 的新实例,因此它会生成相同的数字。所以它会在生成一个新号码之前被调用数千次。如果你这样做:

    Random randomG = new Random();
    public int Fix(int SX)
    {
        SX = randomG.Next(14, 146);
        if (SX % 15 != 0)
        {
            Fix(SX); // This is the recursion
        }           
        return SX;
    }

至少每次你调用它时它都会生成一个新的随机数,这样你就更有可能在堆栈溢出之前命中你想要的一个。

编辑:我忘了提及为什么递归对于实现这一点来说是个坏主意。不要实际使用上面的代码,它仍然是一个糟糕的解决方案。我现在不会修改我的答案,因为更好的答案has been posted

【讨论】:

  • @KevinYoung 有点违反直觉,您可以通过单击“2 天前”链接查看问题的版本历史记录,其中显示“2 天前已编辑”。我不得不说,这个问题在目前的形式下更容易理解,所以我不鼓励你恢复其余的代码。
猜你喜欢
  • 2010-11-27
  • 2022-01-02
相关资源
最近更新 更多