RNGCryptoServiceProvider: generate random numbers in the range [0, randomMax)

冷暖自知 提交于 2020-01-13 03:11:47

问题


I wrote the following code to generate random numbers in the [0, int.MaxValue) range, but I wasn't sure how to restrict the range to [0, randomMax) while maintaining an even distribution:

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

Thanks.


回答1:


Here's one way to do it: http://www.informit.com/guides/content.aspx?g=dotnet&seqNum=775. See the section titled "Creating a System.Random Replacement."

Note, however, that using the modulus operator might not be the best way to ensure a good distribution. A possibly better way would be (int)(NextDouble() * (MaxValue - 1));

Your code has a latent bug. If buffer contains the hex values 00 00 00 80, which is int.MinValue, Math.Abs will throw an exception.

Note that calling GetBytes on the RNGCryptoServiceProvider is very slow compared to calling Random.Next. You're better off calling GetBytes to fill a larger buffer, and then dribble the random numbers from it. My example shows how that's done.




回答2:


There's a description of a suitable algorithm in the Java nextInt documentation.

The algorithm repeatedly rejects any values that will cause an uneven distribution and tries again. This means, theoretically, that in the worst-case scenario it could loop forever. In reality it will be pretty quick.

Here's a (completely untested) C# translation:

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:


An MSDN magazine article shows you how to create a Random that uses the cryptographic RNG:

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

(The article is no longer directly accessible. It was from the September 2007 issue, the title was "Tales from the CryptoRandom". You'll have to download the archived issue. It's .chm so you'll have to unblock it as being from the internet, before you can read it. You could also use the internet archive.)




回答4:


If you don't need much speed, maybe you can try this:

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

This will scale the full-range int number to your range of values, mantaining the distribution.

I know it's an old post, but I've run to a similar problem and this soultion worked for me.



来源:https://stackoverflow.com/questions/6111960/rngcryptoserviceprovider-generate-random-numbers-in-the-range-0-randommax

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!