Professor Mustard Programming

Programming 101 - Unit 06 - Other Features

06a) Random Numbers
Classes and inheritance are all well and good, but sooner or later, most "fun" applications cross paths with random numbers.

In the most absolute sense, there's no such thing as a "random" computed number, because a computer can't "make stuff up". The way random numbers are usually implemented is, you pass a "seed" value into a random number generator (the seed is usually based on the current time), and then the seed is put through some complicated formula that produces a seemingly "random" sequence of numbers. Every time you ask the random number generator for a random number, it gives you the next number in the sequence it calculated.

Of course, the actual sequence of numbers that the generator produced isn't random; it's a series of values that were calculated, using the forumla and your seed value. If you seeded the generator with the same value, it would produce the same sequence of numbers. That's why we seed the generator with the current time: because this way, the value we're seeding with changes every millisecond. Therefore, the potential sequence of numbers we get from the generator changes every millisecond, too! So, can computers truly "make up" random numbers? No. But they can fake it pretty darn well! Of course, algorithmic experts and math purists will argue that humans don't "make up" random numbers, either; it's just that the cognitive processes that they go through to reach those numbers are too complicated and involved to trace. This is why we don't invite algorithmic experts and math purists to parties.

But enough philosophy; let's get down to business. The basics of random number generation couldn't be simpler; the following code will produce a different random integer every time you run the program:

// Getting a random number between 1 and 10
Random r = new Random();
int num = r.Next(1, 11);

From these two lines of code, we can see that random numbers are just a two-step process. The first line of code creates a random number generator, and the second line uses that generator's Next() function to get a random integer, and store it in a variable.

Okay, first off, only initialize the Random object once. Initializing it with "new Random()" sets the random seed based on the current time. If you call "new Random()" multiple times, the algorithm gets re-seeded each time (probably with the same time, if your program runs fast enough), and your results won't look very random at all. It's usually best to simply declare one random object for your program, initialize it once, and call Next() on it as many times as you need. To summarize, say that you wanted 10 random numbers in a row:

// Random generator is initialized 10 times.
for(int i = 0; i < 10; ++i)
  Random r = new Random();
  Console.WriteLine( r.Next(1,11) );

// Good!
// Random generator is initialized ONCE!
Random r = new Random();
for(int i = 0; i < 10; ++i)
  Console.WriteLine( r.Next(1,11) );

Next, let's talk about those numbers we pass into Next(). If we're trying to get a number between 1 and 10, why are we passing in a 1 and an 11? The reason is that the lower bound is inclusive (as in "greater than or equal to"), whereas the upper bound is exclusive (as in "less than"). So for a number between 50 and 100 , you'd pass in 50 and 101. For a number between 25 and 75, you'd pass in 25 and 76. Why does random work this way? It looks kind of stupid when you're dealing with hardcoded numbers, but remember that indexed collections in C# are zero-based. So, if you wanted to pick a random element in an array, you'd simply pass 0 and array.Length in as your arguments to Next().

That's pretty much all there is to random integers. We can get random doubles as well, by using the NextDouble() call, but there's one little twist. The NextDouble() call doesn't take any parameters; it always returns a random double between 0.0 and 1.0. So if we want a number between, say, 75.3 and 90.3, we have to "scale" the result to the appropriate range. We accomplish this by using the following formula, where max and min are the highest and lowest acceptable values to get out of the equation:

double randomNumber = random.NextDouble() * (max-min) + min;

The rationale behind this formula isn't overly complicated (even though it looks kind of scary). Say that we were getting a number between 50.0 and 75.0. The first thing we do is subtract the minimum from the maximum, to get the range of possible values. The range in this case is 25.0 (75 minus 50). When we multiply the random number by this range, we "scale" from a number that falls between 0.0 and 1.0, to number that falls between 0.0 and 25.0. However, we want a number between 50.0 and 75.0, so we're still 50.0 below where we want to be. Because our current random number's minimum is 0.0, we simply add our real minimum, 50.0, to this whole mess, and we end up with our desired result... a number that falls between 50.0 and 75.0.

If you don't feel like understanding all that, you can just use the formula I've provided, but try to absorb it if you possibly can... math is an inescapable fact of life in programming, so you might as well get used to it! The code sample below outlines the use of the Random class.

using System;

namespace A_Random
  class Class1
    static readonly string ln = Environment.NewLine;

    static void Main(string[] args)
      // Declare the random object
      // ONLY DO THIS ONCE; if you call "new Random()" multiple
      // times in the same program, it can screw up the random
      // factor, and you might get the same results, over and over.
      // Declare ONE random object, and use it to call Next() or
      // NextDouble() as many times as you need.
      Random r = new Random();

      // Create some random integers
      // Note that the lower bound is inclusive, but the upper
      // bound is exclusive. In my humble opinion, this is
      // stupid. Still, I didn't write the library, so if you
      // want random numbers you'll just have to deal with it!
      int num = r.Next(1, 11);
      Console.WriteLine("Between one and ten: " + num);

      num = r.Next(20, 501);
      Console.WriteLine("Between twenty and five hundred: " + num);

      num = r.Next(-40, 41);
      Console.WriteLine("Between negative forty and positive forty: " + num);


      // Create some random doubles
      double num2 = r.NextDouble();
      Console.WriteLine("Between 0.0 and 1.0: " + num2);

      // The following formula generates a random number within
      // the bounds of a minimum and maximum:
      // double randomNumber = random.NextDouble() * (max-min) + min;
      // You automatically get this with Next(), but NextDouble()
      // has to be "scaled" from the original value between 0.0 and 1.0.
      num2 = r.NextDouble() * (20 - 10) + 10;
      Console.WriteLine("Between 10.0 and 20.0: " + num2);

      num2 = r.NextDouble() * (0.05 - 0.04) + 0.04;
      Console.WriteLine("Between 0.04 and 0.05: " + num2);


    static void PromptForExit()
      Console.Write(ln + "Program complete! Hit enter to exit...");