C# RNGCryptoServiceProvider:生成范围为[0,randomMax]的随机数

C# RNGCryptoServiceProvider:生成范围为[0,randomMax]的随机数,c#,random,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(b

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

这里有一种方法:。请参阅标题为“创建系统。随机替换”的部分

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

您的代码存在潜在错误。如果
缓冲区
包含十六进制值
0080
,即
int.MinValue
Math.Abs
将引发异常


请注意,在
RNGCryptoServiceProvider
上调用
GetBytes
比调用
Random要慢得多。下一步
。最好调用
GetBytes
来填充一个更大的缓冲区,然后从中提取随机数。我的示例演示了如何完成此操作。

有一个合适算法的描述我在办公室

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

下面是一个(完全未经测试的)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;
}
public static int GetNextInt32(此RNGCryptoServiceProvider rng,int maxValue)
{
如果(最大值<1)
抛出新ArgumentOutOfRangeException(“maxValue”,maxValue,“值必须为正”);
var buffer=新字节[4];
int位,val;
if((maxValue&-maxValue)==maxValue)//maxValue是2的精确幂吗
{
rng.GetBytes(缓冲区);
位=位转换器.ToInt32(缓冲区,0);
返回位&(maxValue-1);
}
做
{
rng.GetBytes(缓冲区);
位=位转换器.ToInt32(缓冲区,0)&0x7FFFFFFF;
val=位%maxValue;
}而(位-val+(最大值-1)<0);
返回val;
}

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


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

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

    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
number,以保持分布


我知道这是一篇老文章,但我遇到了一个类似的问题,这个解决方案对我很有效。

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