【问题标题】:Why do i get a Overflow Exception when trying to run the same method with same parameters?为什么在尝试使用相同参数运行相同方法时会出现溢出异常?
【发布时间】:2018-02-16 12:19:44
【问题描述】:

正在考虑创建一个支持uint, long & ulong 的随机数生成器。我开始制作随机的uint 生成器,但一直在制作最小、最大生成器。

这就是我所拥有的

public class Rand : System.Random

public uint UInt32(uint min, uint max)
{
    byte[] array = new byte[4];

    base.NextBytes(array);

    uint result = BitConverter.ToUInt32(array, 0);

    if (result < min | result > max)
    {
        UInt32(min, max); //here i get a StackOverflowException
    }
    return result;
}

为什么我在尝试使用相同参数运行相同方法时得到StackOverflowException

【问题讨论】:

  • 你有没有试过调试它来一步一步地观察它实际上在做什么,观察变量?您可以回答自己的问题。
  • 基本上你永远不会从递归函数返回。
  • 可能你只是陷入了递归函数循环
  • if (result max) 这是正确的还是应该是 ||?
  • 您生成随机数,如果它不符合标准(最小-最大范围) - 您再次调用相同的函数。但是随机数可能无法匹配这个标准任意次数,而堆栈大小不是无限的,所以经常(多久取决于最小值和最大值)你的递归调用会吃掉所有的堆栈。这不是你应该在一个范围内生成随机数的方式。

标签: c#


【解决方案1】:

您的程序退出递归调用的能力取决于区间 [min..max] 的长度。间隔越小,命中StackOverflowException的概率就越高。

由于您生成的是 4 字节整数,因此您有 232 个可能的值。假设随机值均匀分布,命中minmax之间区间的概率等于(max-min)*2-32。继续递归调用的概率是 1-(max-min)*2-32。如果您希望递归调用有机会在合理的调用次数内结束,那么与 232 相比,minmax 之间的间隔应该相当大。

您可以通过将递归代码转换为迭代代码来避免堆栈溢出,但代价是代码相当慢(您的递归实现也是错误的,因为它会丢弃递归调用的结果,但无论如何都不值得修复)。

【讨论】:

  • 是什么让迭代方法(假设你只是指一个while循环)变慢了?或者你的意思是它通常只是慢而不是比递归版本慢(如果它没有溢出)?
  • 除了你的最后一句话,一切都很好——你不能(轻松地)通过委托给Random(int, int)来实现Random(min, max),更不用说ulong了。特别是对于ulong,如果您想要一个体面的分布,甚至Sample 都不会这样做,因为它在内部只是扩展int。当然有other approaches
  • @Chris 它不会让它变慢,它绝对会变慢。递归代码将与迭代代码一样快。实际上,可以对其进行重组以进行尾调用优化。
  • @JeroenMostert 你可以做Random(int, int) 并将结果转换为(uint) 在未经检查的上下文中。因为 int 和 uint 具有相同的大小 - 可以正常工作(负整数将导致大的正 uint)。对于 ulongs 来说,情况确实不同。
  • @Demiz:算出你的范围大小怎么样。向上取整到最接近的 2 次幂。生成一个介于 0 和 2 次幂之间的随机数。对其进行位掩码。丢弃那些超出范围的(现在低于 50% 的机会超出范围)。将最小值添加到您的数字(将其恢复到实际范围内)。可能有更好的方法来做到这一点,但这至少应该保证您使用相对简单的算法保持一致性。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-15
  • 2012-07-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-13
相关资源
最近更新 更多