【问题标题】:C# Generating random int - mysteryC# 生成随机整数 - 神秘
【发布时间】:2011-12-08 00:39:35
【问题描述】:

我在生成随机整数时发现了一些有趣的事情(至少对我来说),我无法向自己解释,所以我想我会在这里发布。

我的需求很简单:我正在生成随机整数 (Int32) ID,并且旨在最大限度地减少冲突。生成时间不是问题。

我已经尝试过这些生成随机整数的方法:

1.)

   return rnd.Next();

其中 rnd 是方法 #3 中带有种子的 Random 类型的类字段。

2.)

   return rnd.Next(Int32.MinValue, Int32.MaxValue);

其中 rnd 再次是方法 #3 中带有种子的 Random 类型的类字段。

3.)

   var buffer = new byte[sizeof(int)];
   using (var rng = new RNGCryptoServiceProvider())
   {
        rng.GetBytes(buffer);
   }            
   return BitConverter.ToInt32(buffer, 0);

注意:我还尝试在包含类的初始化时将 RNGCryptoServiceProvider 作为类字段初始化一次,以简化 GC 的工作,但我花了同样的时间来生成,所以我认为这会“更随机”。

4.)

   return new Random(Method3()).Next();

5.)

   return new Random(Method3()).Next(Int32.MinValue, Int32.MaxValue);

我知道,在每次调用时创建新的 Random(int seed) 非常耗时,但冲突较少,对吧?

好吧,现在是神秘的部分。我假设大多数碰撞都会有方法#1和#2,其中#1会稍微快一点,更无碰撞,最少的碰撞会有方法#4和#5,其中#4会稍微快一点,更无碰撞和方法# 3 会是某种妥协。

所以我做了一个测试来证明我的假设。我使用每种提到的方法生成了 10 倍(使其平均)一百万个随机数,并计算了平均碰撞次数和生成一百万个数字所需的平均时间。结果(如下)让我有点吃惊。

结果:duration是小时:分钟:秒:毫秒格式

Method1: AvgCollisions: 235, AvgDuration: 00:00:00.3561967
Method2: AvgCollisions: 116, AvgDuration: 00:00:00.4042033
Method3: AvgCollisions: 115, AvgDuration: 00:00:04.6037259
Method4: AvgCollisions: 234, AvgDuration: 00:00:09.2195856
Method5: AvgCollisions: 233, AvgDuration: 00:00:09.1788223

我又跑了几次测试,结果都差不多。

你不觉得奇怪吗?时间并不像我假设的那样令人惊讶,但结果对我来说意味着,方法 2 是生成随机数的最佳方法,因为它是最随机、最快的,您可以设置最小和最大生成数.不知道 Method2 比 Method3 更可预测多少,因为我不知道如何测试它。

谁能解释我做错了什么或者为什么方法#4和#5没有最少的碰撞,为什么碰撞的百分比总是相同的?不应该是随机的吗?

编辑: 这是我完成的这个测试的 Visual Studio 2010 解决方案:http://bit.ly/nxLODw

【问题讨论】:

  • 您确定在第一个结果中看到的碰撞次数吗?鉴于this(以及这里的无数其他类似讨论——这是前五个编程误解之一),其他一切都是有道理的。你能发布你的测试用例吗?
  • 您的#4/#5 假设是错误的。你永远不应该为每个随机数的需要调用new Random()。只做一次。
  • +1 为迈克尔·佩罗塔。 and a zillion other similar discussions here
  • 致 Michael Petrotta:我发布了 VS2010 soultion,它正在编辑中。我知道 Random 类默认由系统时钟播种,这就是为什么我每次都从 RNGCryptoServiceProvider 给它新的随机种子,还是意味着它在每次 Next() 调用中使用系统时钟来计算?

标签: c# random generator


【解决方案1】:

唯一奇怪的行为是方法 5。

在方法 1、4 中,您生成一个 0 到 int.MaxValue 范围内的数字。

在方法 2、3 和 5 中,您生成一个 int.MinValue 到 int.MaxValue 范围内的数字。

因此,对于方法 2 和 3,您的范围大约是方法 1、4 的两倍,并且它们的碰撞次数大约是方法 1、4 的一半。这对我来说似乎很正常。

那么为什么方法 5 会产生与方法 1 和 4 一样多的冲突,即使它生成的数字范围更大?好吧,事实证明System.Random 构造函数采用种子的绝对值。换句话说,它将随机序列的数量减少到一半。因此,即使您从更大范围内获得数字,您也可以从更少的不同序列中生成它们。

【讨论】:

  • 你明白了。不知道我怎么会错过 1 和 4 只生成正数。现在这对我来说很有意义,谢谢。
  • 现在提出一个新问题,为什么所有方法的碰撞次数都相同?方法3不应该有最少的碰撞吗?因为它是 RNGCrypto...
  • 并非如此。即使在“真正的”随机序列中也会发生碰撞。由于您只生成了大约 1/2000 或 1/4000 的可能数字,因此您的密度太低,看不出任何差异。
  • 关于 System.Random 和 RNGCrypto 之间的区别...:主要区别在于目的。 System.Random 是通用的,而 RNGCrypto... 是专门为破坏序列的重建而设计的。许多其他 RNG,您可以从序列中有限数量的值重建序列(并因此预测序列的其余部分)。
【解决方案2】:

当您在那里时,您可能希望将 #3 修改为:

        var buffer = new byte[sizeof(int)];

        using (var rng = new RNGCryptoServiceProvider())
        {
            rng.GetBytes(buffer);
        }

        return BitConverter.ToInt32(buffer, 0);

这 a) 保证您的数组是 int 的大小(而不是幻数 4)并且 b) 正确处理 RNGCryptoServiceProvider 是的 IDisposable

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-10-09
    • 2014-08-22
    • 2015-08-05
    • 1970-01-01
    • 1970-01-01
    • 2010-10-23
    • 2012-03-18
    相关资源
    最近更新 更多