【问题标题】:RNGCryptoServiceProvider: generate random numbers in the range [0, randomMax)RNGCryptoServiceProvider:生成[0, randomMax)范围内的随机数
【发布时间】:2011-05-24 14:25:22
【问题描述】:

我编写了以下代码来生成 [0, int.MaxValue) 范围内的随机数,但我不确定如何在保持均匀分布的同时将范围限制为 [0, randomMax):

private static int GetNextInt32(this RNGCryptoServiceProvider random)
{
  var buffer = new byte[sizeof(int)];
  random.GetBytes(buffer);
  return Math.Abs(BitConverter.ToInt32(buffer, 0));
}

谢谢。

【问题讨论】:

    标签: c# random


    【解决方案1】:

    这是一种方法:http://www.informit.com/guides/content.aspx?g=dotnet&seqNum=775。请参阅标题为“创建 System.Random 替换”的部分。

    但是请注意,使用模数运算符可能不是确保良好分布的最佳方式。一个可能更好的方法是(int)(NextDouble() * (MaxValue - 1));

    您的代码存在潜在错误。如果buffer包含十六进制值00 00 00 80,即int.MinValue,Math.Abs会抛出异常。

    请注意,与调用Random.Next 相比,在RNGCryptoServiceProvider 上调用GetBytes 非常慢。你最好调用GetBytes 来填充更大的缓冲区,然后从中运出随机数。我的示例显示了这是如何完成的。

    【讨论】:

    • 吉姆,Next() % maxValue 有偏见;举个简单的例子,如果 Next() 返回 0、1、2 或 3 均匀分布,Next() % 3 将返回 0 的频率是 1 或 2 的两倍。
    • @Rawling:是的,我确实提到使用模运算符可能是个问题。感谢您提供具体示例。
    • 抱歉,我没有看到你的警告。
    【解决方案2】:

    在Java nextInt documentation中有一个合适算法的描述。

    该算法反复拒绝任何会导致分布不均的值并再次尝试。从理论上讲,这意味着在最坏的情况下,它可能会永远循环。实际上它会很快。

    这是一个(完全未经测试的)C# 翻译:

    public static int GetNextInt32(this RNGCryptoServiceProvider rng, int maxValue)
    {
        if (maxValue < 1)
            throw new ArgumentOutOfRangeException("maxValue", maxValue, "Value must be positive.");
    
        var buffer = new byte[4];
        int bits, val;
    
        if ((maxValue & -maxValue) == maxValue)  // is maxValue an exact power of 2
        {
            rng.GetBytes(buffer);
            bits = BitConverter.ToInt32(buffer, 0);
            return bits & (maxValue - 1);
        }
    
        do
        {
            rng.GetBytes(buffer);
            bits = BitConverter.ToInt32(buffer, 0) & 0x7FFFFFFF;
            val = bits % maxValue;
        } while (bits - val + (maxValue - 1) < 0);
    
        return val;
    }
    

    【讨论】:

      【解决方案3】:

      一篇 MSDN 杂志文章向您展示了如何创建使用加密 RNG 的 Random:

      http://msdn.microsoft.com/en-us/magazine/cc163367.aspx

      (不再直接访问该文章。它来自 2007 年 9 月的问题,标题是“来自 CryptoRandom 的故事”。您必须下载存档的问题。它是 .chm,因此您必须取消阻止它是来自互联网,在您阅读之前。您也可以使用互联网存档。)

      【讨论】:

      【解决方案4】:

      如果你不需要太多的速度,也许你可以试试这个:

          private static RNGCryptoServiceProvider _RNG = new RNGCryptoServiceProvider();  
      
          private static int GetNextRnd (int min, int max)
          {
              byte[] rndBytes = new byte[4];
              _RNG.GetBytes(rndBytes);
              int rand = BitConverter.ToInt32(rndBytes, 0); 
              const Decimal OldRange = (Decimal)int.MaxValue - (Decimal)int.MinValue;
              Decimal NewRange = max - min;
              Decimal NewValue = ((Decimal)rand - (Decimal)int.MinValue) / OldRange * NewRange + (Decimal)min;
              return (int)NewValue;
          }
      

      这会将全范围 int 数字缩放到您的值范围,从而保持分布。

      我知道这是一篇旧帖子,但我遇到了类似的问题,这个灵魂对我有用。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2010-10-20
        • 2010-12-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-03-01
        • 2016-05-04
        相关资源
        最近更新 更多