Another useful class provided by the JDK is Random. This class gives you the ability to generate pseudorandom numbers. The Random class is a great example of encapsulation in object oriented programming. Performing high quality pseudorandom number generation can be tricky. I should not need to implement an algorithm when high quality an thoroughly debugged algorithms exist already. I can understand what the algorithm produces without knowing the specific details for how the algorithm works. The Random class encapsulates these algorithms and exposes them as simple methods. Let’s explore a bit more of what Random can do.
Subsection3.5.1Instantiating a Random Object
Pseudorandom numbers are just that — not truly random, but rather an approximation of true randomness. While they appear random, that are generated in a well-defined order using a deterministic algorithm. A sequence of pseudorandom numbers is repeated when it starts at the same seed value, which has type long. A Random object may be instantiated with a seed as the parameter, or with no parameters at all. If no seed is specified in the Random constructor, then one is selected for you, usually derived from current value of the running JVM’s high-resolution time source.
Like Scanner, the Random class is not available automatically in java. It must first be imported from java.util.Random. A Random object is instantiated using the new keyword and the standard constructor syntax. Once instantiated, the Random object can be used repeatedly to generate various types of random numbers.
Random rnd1 = new Random(123L); // Create Random object with seed 123L
Random rnd2 = new Random(); // Create Random object with automatic seed
Listing3.5.1.Random constructor examples.
Subsection3.5.2Random Methods
Random object methods give us the ability to generate a variety of different random number types, selected from uniform and normal (Gaussian) distributions. The following table describes many of these methods.
Table3.5.2.Several Random Methods
Method
Returns
Description
nextInt(int max)
int
Compute a uniformly distributed pseudorandom int in [0, max). Inclusive of 0 but exclusive of max.
nextDouble()
double
Compute a uniformly distributed pseudorandom double in [0.0, 1.0).
nextBoolean()
boolean
Compute an equally distributed pseudorandom boolean in {true, false}.
nextGaussian()
double
Compute a normally distributed pseudorandom double with mean 0.0 and standard deviation 1.0
setSeed(long seed)
void
Reset the seed of the pseudorandom number generator.
Subsection3.5.3Random Example
In the following example program we create a Random object and assign it to the variable rnd. Then we use the object to test the nextInt(…), nextDouble(), and nextGaussian() methods.
nextInt(…)intexclusiveinclusivernd.nextInt(10)
// RandomExample.java
import java.util.Random;
public class RandomExample {
public static void main(String[] args) {
int i1, i2;
double x1, x2, x3;
Random rnd = new Random();
// Pseudorandom ints in range [0, 10)
// The value 10, is out of range
i1 = rnd.nextInt(10);
i2 = rnd.nextInt(10);
System.out.println(i1 + ", " + i2);
// Pseudorandom doubles in range [0.0, 1.0)
x1 = rnd.nextDouble();
x2 = rnd.nextDouble();
System.out.println(x1 + ", " + x2);
// Pseudorandom double with mean 0.0
// and standard deviation 1.0
x1 = rnd.nextGaussian();
x2 = rnd.nextGaussian();
x3 = rnd.nextGaussian();
System.out.println(x1 + ", " + x2 + ", " + x3);
}
}
While the Random object methods give us the basis for generating pseudorandom numbers, practical applications will require us to modify the range within which the numbers are generated to suit our application requirements. Specifically, we’ll often need to scale random number ranges to make them wider or more narrow, and we’ll often have to translate (shift) ranges to higher and lower values. It is fortunate that the pseudorandom number generator methods include 0 at one end of the range of generated numbers. As you’ll see, this is convenient when manipulating these numeric ranges.
The easist way to scale and translate pseudorandom number ranges is to operate on their lower and higher bounds separately. If we modify the bounds correctly, the inner values of the range will follow. We can easily modify the width of a range by multiplying values generated by a suitable scale factor. For example, if we are interested in a pseudorandom double in the range [0.0, 2.0) we would multiply values returned from nextDouble() by 2.0, as follows. We know that the newly generated values will fall in the range [0.0, 2.0).
Table3.5.4.
\(2.0 \times [0.0, 1.0)\)
\([2.0\times0.0, 2.0\times1.0)\)
\([0.0, 2.0)\)
Similarly, we can add a constant to a range to translate it to a new location.
Consider the following examples. In all cases assume rnd is a previously instantiated Random object.
Activity3.5.1.
Write a Java expression that returns a uniformly distributed double in the range [-1.0, 1.0).
Watch out for the lower bound; it is inclusive of -10. The range includes 6 integers and is shifted. Multiplying by a negative number causes the bounds of a range to swap roles.