CS302 Lecture notes -- Simulating Random Events


Event Generation

One of the trickiest parts of writing a simulator is choosing how you generate random events. Suppose I say that the average transaction time is 10 minutes. The following sequence of transactions fits this description:
     8 12 10 11 9 7 13
As does this sequence:
     0 0 0 0 0 0 0 0 0 100
These two sequences are greatly different, even though they both average to ten.

Analyzing numerical sequences is a big area in statistics, and there is quite a bit of math involved, almost all of which is beyond the scope of this lab. However, some statistical terms and concepts will be useful.

Random numbers are defined to fit what are known as distributions. These define how we can characterize the random numbers in more ways than simply a mean value. We will use two such distributions in our simulation. The first is a very simple distribution called a uniform distribution. For our purposes, if we choose a random number r according to a uniform distribution with a mean m, this means that r will have a value between 0 and 2m, and that every value between 0 and 2m is equally likely.

For example, suppose that our random numbers are integers. If we are choosing random numbers according to a uniform distribution with mean 3, then each time we choose a random number, it is equally likely that this number will be 0, 1, 2, 3, 4, 5, and 6.

Uniform distributions are very easy to use in C. There are two important functions as part of the C standard library: srand48() and drand48(). srand48(i) takes an integer i and uses it as a seed to the random number generator. Then each time you call drand48() it returns a double uniformly distributed in the range [0, 1). Thus, it is a random number that follows a uniform distribution with a mean of 0.5.

Therefore, if you want to get a random number according to a uniform distribution with a mean of m, you simply call drand48() and multiply the result by 2m:

    /* generate 20 random numbers using a uniform distribution with a mean
       of 50 */
    srand48(737);   // provide an initial "seed" to the random generator
    for (i = 0; i < 20; i++) {
      d = drand48()*mean*2;
      printf("%d \t %lf \n", i, d);
    }

A complete program to generate random numbers according to a uniform distribution is here: uniform.cpp.


We can view distribution functions using histograms. For example the following histogram shows a uniform distribution function whose mean is 5:
You can read this in the following way: If you choose a random value, the probability of it being between 0 and 1 is 0.1. Similarly, the probability of it being between 1 and 2 is 0.1. Etc.

Different distribution functions have diffierent histograms. A very important distribution function is the exponential distribution function. This distribution function has one parameter called lambda which is one over the mean of the distribution function. Here is a histogram of an exponential whose mean is 120:

Now, this is awfully hard to make any sense of. If we instead plot the histogram on a log axis, it looks a little more palatable.
Cool, no? Now, the exponential is very important, because many real-life phenomena follow an exponential distribution. For example, light bulbs fail according to an exponential distribution. Moreover, most queueing situations (i.e. people entering a bank, cars arriving at a stop light) follow exponential distributions.

In our simulation, we are going to assume that transaction times (i.e. how long transactions take) follow a uniform distribution and the arrival times of customers follow an exponential distribution.


Implementing Histogram Distributions

Unfortunately, there currently are no standard implementations in C++ that allow us to generate numbers from an arbitrary distributions. But, there is a fairly straightforward way that we can implement our own random number generator based on histograms. This approach will work for any histogram distribution we define, which means that we now have an implementation for generating a random number from any arbitrary distribution. Here's the general idea of how it works. What we want to do is to come up with a mapping from a uniform distribution to our arbitrary distribution. Bigger "chunks" of the uniform distribution will map to values that are more likely to occur in our arbitrary distribution, like we see in the following figure:
Our detailed approach is as follows. We assume we've already created a file that gives us a histogram of our arbitrary distribution. An example histogram file that gives the exponential with a mean of 120 is in expon_120. In this file, each line gives an x and y value. The x value is the middle of a histogram bar, and the y value is the height of the bar. The y values are relative frequences. In other words, they do not have to sum to one. For example, here is a histogram file for the uniform distribution with a mean of 1:
0  1
1  1
2  1
In other words, each value between 0 and 2 is equally likely.

Algorithm to set up histogram random number generator:

Based on the above, we set up our histogram random number generator as follows:

Algorithm to generate next histogram random number:

Whenever we need a new random number generated according to this histogram distribution, we do the following: That's it!!