RNGCryptoServiceProvider:生成[0,randomMax]范围内的随机数

时间:2011-05-24 14:25:22

标签: c# random

我编写了以下代码来生成[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));
}

感谢。

4 个答案:

答案 0 :(得分:4)

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

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

您的代码有一个潜在的错误。如果buffer包含十六进制值00 00 00 80,即int.MinValueMath.Abs将引发异常。

请注意,与调用GetBytes相比,RNGCryptoServiceProvider上的Random.Next调用非常慢。你最好调用GetBytes来填充更大的缓冲区,然后从中运出随机数。我的例子说明了这是怎么做的。

答案 1 :(得分:3)

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;
}

答案 2 :(得分:1)

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

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

(这篇文章不再可以直接访问了。它来自2007年9月号,标题是“来自CryptoRandom的故事”。你必须下载存档的问题。它是.chm所以你必须解锁它来自互联网,在您阅读之前。您也可以使用互联网档案。)

答案 3 :(得分:0)

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

    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数字缩放到您的值范围,保持分布。

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