Random Numbers in C#
Random number generators written in code (or any deterministic algorithm) aren't really random. You could go as far as to say that unless based on nondeterministic events at a quantum level, there will (theoretically) be a way of predicting the next number in a series. Taking it this far might sound silly, but it gets the point across.
Random number algorithms are good at generating a series of numbers which appear random. All start with a seed value n, where n is really random number #1. n then becomes the seed for the next number in the series (random number #2), or n.Next. Start two or more generators with the same seed, and each will generate the same series of numbers.
Random number generators based in hardware (e.g. on a PCI card) use natural phenomenon like EMF noise from a crystal. Where atomic clocks measure time by measuring events from materials with regular, predictable properties of decay or emission, hardware number generators just use materials which would make for awful clocks; the unpredictability of the material makes it a good random number generator. But let's get back to .NET and stuff you can actually use.
.NET provides a Random object. It uses a deterministic algorithm. Seed it twice with the same value and Random.Next will deliver the same output. If you create a Random object every time you need a single random number, you're not taking advantage of the algorithm, you're creating instances, not a series. Using the system clock as a seed value is fine -- numbers will appear random to you -- until you create two Random objects during the same clock tick. That didn't happen a few years ago, but today machines work fast enough that this is an issue.
To solve the problem, take advantage of the fact that Random is good at producing a series. Use the same instance of Random from call to call, number to number, and your n.Next will churn out reasonably random series. How do you do that? Cache it! The following chunk of code returns a Random object ready for calling .Next, and caches it from request to request:
/// <summary>
/// Get a Random object which is cached between requests.
/// The advantage over creating the object locally is that the .Next
/// call will always return a new value. If creating several times locally
/// with a generated seed (like millisecond ticks), the same number can be
/// returned.
/// </summary>
/// <returns>A Random object which is cached between calls.</returns>
public static Random GetRandom(int seed)
{
Random r = (Random)HttpContext.Current.Cache.Get("RandomNumber");
if (r == null) {
if (seed == 0)
r = new Random();
else r = new Random(seed);
HttpContext.Current.Cache.Insert("RandomNumber",r);
}
return r;
}
/// <summary>
/// GetRandom with no parameters.
/// </summary>
/// <returns>A Random object which is cached between calls.</returns>
public static Random GetRandom()
{
return GetRandom(0);
}
And this is the code that calls it:
Random r = GetRandom(); // Produce a number from 00000 through 99999
lock (r)
{
String myNumber = r.Next(0,99999).ToString("00000");
}
[Update 2004-05-06: Added a lock {} around the code which executes r.Next() according to the recommendation in Brad Abrams weblog.]
[Update 2005-07-05: Cleaned up the text slightly and double-checked that improvements from the comments are in there. This entry now has over 32,000 hits now, may as well make it the last stop you need to make.]