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.
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 values00 00 00 80
, which isint.MinValue
,Math.Abs
will throw an exception.Note that calling
GetBytes
on theRNGCryptoServiceProvider
is very slow compared to callingRandom.Next
. You're better off callingGetBytes
to fill a larger buffer, and then dribble the random numbers from it. My example shows how that's done.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:
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.)
If you don't need much speed, maybe you can try this:
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.