Random Number Generators


 
Since random number generators are useful, and are used in many assignments, we will highlight a few aspects of random number generation, starting with the most basic generator: (source file )


public class uniform_random {
  // Basic Lehmer generator - constants
  static final long m = 2147483647L;
  static final long a = 48271L;
  static final long q = 44488L;
  static final long r = 3399L;

  // "Global" variable - the seed, set to some 
  // arbitrary non-zero value.
  static long r_seed = 12345678L; 

  // Basic Lehmer generator - uniform[0,1]
  // For more information see Knuth, Vol. II.
  public static double uniform ()
  {
    long hi = r_seed / q;
    long lo = r_seed - q * hi;
    long t = a * lo - r * hi;
    if (t > 0)
      r_seed = t;
    else
      r_seed = t + m;
    return ( (double) r_seed / (double) m );
  }

  public static void main (String[] argv)
  {
    // Let's test the generator. The mean should be 0.5.
    double sum = 0;
    for (int i=1; i<=10000; i++)
      sum += uniform ();

    System.out.println ("Average of 10000 samples: " + sum/10000);
  }

}

This generator is an example of the well-known Lehmer class of random number generators:

 

Consider the following modification to the above code: (source file )


public class uniform_random {
  // Basic Lehmer generator - constants
  static final long m = 2147483647L;
  static final long a = 48271L;
  static final long q = 44488L;
  static final long r = 3399L;

  // "Global" variable - the seed, set to some 
  // arbitrary non-zero value.
  static long r_seed = 12345678L; 

  // Basic Lehmer generator - uniform[0,1]
  // For more information see Knuth, Vol. II.
  public static double uniform ()
  {
    long hi = r_seed / q;
    long lo = r_seed - q * hi;
    long t = a * lo - r * hi;
    if (t > 0)
      r_seed = t;
    else
      r_seed = t + m;

    double returnValue = ( (double) r_seed / (double) m );
    System.out.println ("r_seed=" + r_seed + " m=" + m + " returnValue=" + returnValue);   
    return returnValue;
  }

  public static void main (String[] argv)
  {
    // Print out 10 values.
    for (int i=1; i<=10; i++)
      uniform ();
  }

}
Here's what gets printed out:
r_seed=1085252519 m=2147483647 returnValue=0.5053600852868334
r_seed=508259731 m=2147483647 returnValue=0.23667688073435653
r_seed=1352291773 m=2147483647 returnValue=0.6297099281240767
r_seed=1563240271 m=2147483647 returnValue=0.7279404773041329
r_seed=890733155 m=2147483647 returnValue=0.414779947798131
r_seed=1810028418 m=2147483647 returnValue=0.8428601635819581
r_seed=1509587083 m=2147483647 returnValue=0.7029562647002545
r_seed=862973489 m=2147483647 returnValue=0.4018533459873187
r_seed=1852986660 m=2147483647 returnValue=0.8628641538614706
r_seed=677683663 m=2147483647 returnValue=0.3155710470469534
 

Now, it's important to realize that the random numbers so produced have the property of being uniform:

 

Next, an obvious question is: what should the initial value of r_seed be?
Answer:

If you execute the file once, you get the output:

r_seed=1085252519 m=2147483647 returnValue=0.5053600852868334
r_seed=508259731 m=2147483647 returnValue=0.23667688073435653
r_seed=1352291773 m=2147483647 returnValue=0.6297099281240767
r_seed=1563240271 m=2147483647 returnValue=0.7279404773041329
r_seed=890733155 m=2147483647 returnValue=0.414779947798131
r_seed=1810028418 m=2147483647 returnValue=0.8428601635819581
r_seed=1509587083 m=2147483647 returnValue=0.7029562647002545
r_seed=862973489 m=2147483647 returnValue=0.4018533459873187
r_seed=1852986660 m=2147483647 returnValue=0.8628641538614706
r_seed=677683663 m=2147483647 returnValue=0.3155710470469534
Now if you execute again, you get:
r_seed=1085252519 m=2147483647 returnValue=0.5053600852868334
r_seed=508259731 m=2147483647 returnValue=0.23667688073435653
r_seed=1352291773 m=2147483647 returnValue=0.6297099281240767
r_seed=1563240271 m=2147483647 returnValue=0.7279404773041329
r_seed=890733155 m=2147483647 returnValue=0.414779947798131
r_seed=1810028418 m=2147483647 returnValue=0.8428601635819581
r_seed=1509587083 m=2147483647 returnValue=0.7029562647002545
r_seed=862973489 m=2147483647 returnValue=0.4018533459873187
r_seed=1852986660 m=2147483647 returnValue=0.8628641538614706
r_seed=677683663 m=2147483647 returnValue=0.3155710470469534
It's the same output! So, how can we call this random?

Here's what's going on:

Thus, to perform estimation, we will need to run an experiment multiple times and take an average:
 

As an example, consider the following experiment:

Here is the code: (source file )

public class random_example2 {

  static final long m = 2147483647L;
  static final long a = 48271L;
  static final long q = 44488L;
  static final long r = 3399L;

  static long r_seed = 12345678L; 

  public static double uniform ()
  {
    long hi = r_seed / q;
    long lo = r_seed - q * hi;
    long t = a * lo - r * hi;
    if (t > 0)
      r_seed = t;
    else
      r_seed = t + m;
    return ( (double) r_seed / (double) m );
  }

  public static void main (String[] argv)
  {
    double[] A = new double [5];
    int numArrays = 10000;
    int numOccurences = 0;

    for (int n=1; n<=numArrays; n++) {
      for (int i=0; i<5; i++) 
        A[i] = uniform();

      // Check for property.
      boolean property = true;
      for (int i=1; i<5; i++) {
        if (A[i] < A[i-1])
          property = false;
      }

      // Count.
      if (property)
        numOccurences++;
    }
    
    // Now estimate probability.
    double prob = (double) numOccurences / (double) numArrays;
    System.out.println ("Probability estimate: " + prob);
  }

}