Blogus Maximus

Rubbing people the wrong way since 1970...

  Home  |   Contact  |   Syndication    |   Login
  1366 Posts | 10 Stories | 2226 Comments | 1336 Trackbacks

News


Google My Blog

Catch me at: The List!


My InstallScript Utility Belt My Amazon Wishlist
My Standard Disclaimer

Tag Cloud


Archives

Post Categories

Image Galleries

Blogs

Code Camps

CTown Geeks

Geeky Webcomics

High Geek

Magenic Blogs

Microsoft Blogs

My Articles

My Sites

PodCasts

UG

XNA

Ok, everybody already knows the default Random Number Generator in .NET stinks for anything other than really simple usage. If this is news to you, take a moment and go read this article first, then come back here.

In HA! I use a C# implementation of the Mersenne Twister for RNG, but there is another option. After seeing John Lunsford's excellent Crypto presentation at the Columbia, SC .NET User Group, I decided to start playing with the RandomNumberGenerator class in the System.Security.Cryptography namespace.

I haven't done a comprehensive analysis on performance yet, but "they say" that the Mersenne Twister is faster and more random. Depending on your needs, the Mersenne Twister might still be the better way to go. However, both of these are so much better than System.Random, and the differences between them so negligible, that it might not matter. Depends on your needs, of course.

The Crypto stuff works like this:

            Dim b(10) As Byte
            System.Security.Cryptography.RandomNumberGenerator.Create.GetNonZeroBytes(b)

That gives me an array of 10 numbers, each between 1 and 255. There is another method, called GetBytes that includes 0 in the results.

Of course, if you want a range OTHER than 1 - 255, you have to do a little extra work. Let's say you want numbers in the range of 1 - 20, all you do is MOD the resulting number by 20 and then add 1. The following code will take each value and adjust it down to the specified range:

            For intCtr = 0 To 9
                b(intCtr) = (b(intCtr) Mod 20) + 1
            Next

Now you have an array of 10 numbers, each between 1 and 20. What you do with them at this point is largely up to you. If you need pseudo-random numbers larger than 255, then I think you're definitely better off using the Mersenne Twister. For simulating die rolls, such as 1-4, 1-6, 1-8, 1-10, 1-12, 1-20 or 1-100 this methods works great, is extremely simple to use and doesn't require the additional effort of adding something as complex as a Mersenne Twister to your code.

posted on Sunday, October 16, 2005 10:39 AM

Feedback

# re: Using Crypto for your Random Numbers in VB.NET 10/17/2005 2:45 PM Zman
Using Mod unfortunatly will bias your 'random' numbers becuase 255 doesn't divide evenly by 20. You will get 1-16 slightly more often than you get 17-20.

# re: Using Crypto for your Random Numbers in VB.NET 10/17/2005 3:49 PM Chris Williams
Well that's definitely a problem... I guess it's back to the Twister. Or do you have another suggestion?


# re: Using Crypto for your Random Numbers in VB.NET 10/17/2005 4:02 PM Chris Williams
FWIW, I did 20 sets of 100 numbers (range: 1-20) and looked for the 20s each time. Every set but one had 4-6 occurrences of the number 20 in the result set. The one set was a fluke, and had 2 occurrences of the number 20.

All things considered, it seems like a pretty even distribution.

After all, if you are rolling a 20 sided die, you have a 5% chance of getting a 20 on each roll.

# re: Using Crypto for your Random Numbers in VB.NET 10/17/2005 8:05 PM Zman
Its close, but its not even.

You have 256 possible values and you are trying to fit them into 20 buckets.

YOu don't have to do a big sample. Just put every number from 0...255 into your function (x mod 20) +1. The numbers 1-16 will be returned 13 times, but the numbers 17-20 only 12.

So the chance of getting a 1 (or 2-16) is 13/256 = 5.078125% and the chance of a 17 (or 18-20) is 12/256 = 4.6875%

The problem isn't the random number generator. Its that you only have 255 inputs into whtever formula you have and you have to share those 255 between your 20 output values so no matter how you do it you will HAVE to have 4 numbers that come back 12 times and 16 numbers that come back 13 times.

So how to fix it ? Back to the mersenne primes is one way. Or just use the bytes that come back as a long - get 4 bytes from 0 to 255 giveing you a much bigger range into your mod function. Yes there will still be a slight bias but it gets smaller the bigger range you have to start with.






# re: Using Crypto for your Random Numbers in VB.NET 10/17/2005 8:19 PM Chris Williams
Ok, that makes sense. I'll have to play with that some and see what I get. It definitely beats implementing a Mersenne Twister from scratch. If I were so inclined... :)

# re: Using Crypto for your Random Numbers in VB.NET 10/19/2005 11:08 AM Zman
Implement from scratch?? Bad developer... reusue, reuse, reuse there are several sampes out there if you type

mersenne twister c#

into google

(says andy carefully trying to avoid typing a URL!)

# re: Using Crypto for your Random Numbers in VB.NET 10/19/2005 11:10 AM Chris Williams
Well yeah... I know that. I'm already using a C# Mersenne Twister in HA! (have been for months now)

I just meant that alternatives are always good.

# re: Using Crypto for your Random Numbers in VB.NET 2/25/2006 6:47 AM supra
Can ne 1 help me? The problem is every time i clicked button the test doesn't changed.
For m As Integer = 0 To 3
For n As Integer = 0 To 3
i = r.Next(1, 10)
textboxGrid(m, n).Text = i
Next n
Next m


# re: Using Crypto for your Random Numbers in VB.NET 3/17/2006 4:35 PM Robbie
Can't you just discard any byte that is above 240?

i.e if getbytes returns 255, discard it and call it again, repeat until you get a number between 1 and 240.

Then you can use Mod and still have an even distribution.



# re: Using Crypto for your Random Numbers in VB.NET 4/14/2010 2:25 AM Mhano
// get a double between 0.0 and 1.0 using a secure random number generator and the maximum fidelity of a double

var rndBytes = new byte[8];
RandomNumberGenerator.Create().GetBytes(rndBytes);
ulong uIntResult = BitConverter.ToUInt64(rndBytes, 0);
double doubleResult = (double)(uIntResult / (decimal)UInt64.MaxValue);

// get a value between min and max exlusive of max (like System.Random.Next(int minValue, int maxValue)

int intresult = (int)Math.Min(Math.Max(Math.Round(minValue + (NextDouble() * (maxValue-minValue)) - 0.5 , 0, MidpointRounding.AwayFromZero), minValue), maxValue);

# re: Using Crypto for your Random Numbers in VB.NET 4/14/2010 2:29 AM Mhano
// Correction, it should have read doubleResult not NextDouble()

int intresult = (int)Math.Min(Math.Max(Math.Round(minValue + (doubleResult * (maxValue-minValue)) - 0.5 , 0, MidpointRounding.AwayFromZero), minValue), maxValue);

Post A Comment
Title:
Name:
Email:
Comment:
Verification: