Random Number Generation in Java
A random number is a number that is almost impossible to predict, like the result of throwing a dice. A random number generator can provide you with such a number when you need it, and you will probably need it quite often. For example, it comes in handy when you're trying to create a password nobody can guess, make the next unpredictable move in a game, or generate a lot of random data for testing purposes. Random generators are widely used in cryptography, machine learning, games, and more.
Pseudorandom numbers and seeding
Before we start exploring how to generate random numbers in Java, let's focus on one important point. The random numbers we are going to discuss aren't truly random because they can always be determined by an initial value called seed.
Every time we get a new random number, we actually get the next number in a predefined sequence. These numbers are often called pseudo-random numbers, and they are not completely unpredictable! We can calculate them all if we know the initial value and the algorithm of the sequence. That initial value is called a seed.
It is guaranteed that the same seed produces the same sequence if the same Java runtime version is used because the algorithm is the same. However, the generated pseudo-random number is good enough for practical tasks. These generators are quite important because of their speed in number generation and their reproducibility.
Creating a pseudorandom generator
Java provides the Random
class to generate pseudo-random values of different types, such as int
, long
, double
, and even boolean
. For now, we will use this class only for numbers.
First of all, we need to import it:
import java.util.Random;
We have two constructors to create an object of this class:
Random()
creates a new random generator and sets the seed of the generator to a value that is very likely to be distinct from any other invocation of this constructor:
Random random = new Random();
Random(long seed)
creates a new random generator with the specified initial value of its internal state:
Random random = new Random(100000);
If we don't specify a seed, the generator will give us a new sequence every time. But if we specify the seed, the sequence will be calculated based on it.
Regardless of what constructor we used, we have a generator called random
that can produce random numbers.
The basic methods
After we've created a generator, we can invoke one of the following methods of it:
int nextInt()
returns a pseudorandom value of theint
type;int nextInt(int n)
returns a pseudorandom value ofint
type in the range from0
(inclusive) ton
(exclusive);long nextLong()
returns a pseudorandom value oflong
type;double nextDouble()
returns a pseudorandom value ofdouble
type between0.0
and1.0
;void nextBytes(byte[] bytes)
generates random bytes and places them into a user-supplied byte array.
All the listed methods produce uniformly distributed values.
Let's take a look at an example:
Random random = new Random();
System.out.println(random.nextInt(5)); // it may print 0, 1, 2, 3, 4
If we start this code multiple times, the result is different (or it may happen to be the same).
If we need to reproduce the same sequence of random numbers, we may specify a seed to the constructor:
Random random = new Random(100000);
System.out.println(random.nextInt(5)); // it may print 0, 1, 2, 3, 4
System.out.println(random.nextInt(5)); // it may print 0, 1, 2, 3, 4
In this case, while starting the program multiple times, we will always get the same numbers in the output.
Note: An object of the Random
class can generate Gaussian distributed pseudorandom double numbers by invoking the nextGaussian()
method. This distribution may be required for some statistical analysis and machine learning applications, but it is not that common in general programming.
An example: printing pseudorandom numbers
Let's suppose that we need a program that prints out a specified number of pseudorandom integers within a given range (boundaries included). Unfortunately, the Random
class does not provide a method to generate numbers in a range. Let's use it as an opportunity to practice and create it from scratch!
As you remember, the nextInt(n)
method produces a pseudorandom integer from 0
(inclusive) to n
(exclusive).
We want to use it to generate numbers within a specific range, for example, from 2 to 5, including both boundaries.
Let's take the length of the interval plus one: 5 – 2 + 1 = 4. We can use this interval to generate any number from 0 to 3 by using the nextInt(4)
method.
Now imagine that we shift the interval to the value of the lower border as we need. In our case, we will shift the lower range to 2.
This way, we can generate any numbers from 2 to 5, including both boundaries.
The illustrated idea can be implemented by a simple code line:
int next = random.nextInt(upper - lower + 1) + lower;
Here is the complete program that prints 4 pseudorandom integers within a given range:
import java.util.*;
public class RandomNumbersDemo {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int lower = scanner.nextInt();
int upper = scanner.nextInt();
Random random = new Random();
int intervalLength = upper - lower + 1;
System.out.println(random.nextInt(intervalLength) + lower);
System.out.println(random.nextInt(intervalLength) + lower);
System.out.println(random.nextInt(intervalLength) + lower);
System.out.println(random.nextInt(intervalLength) + lower);
}
}
For example, if we have to generate numbers exactly within the range from 20 to 30 (inclusive):
20 30
The output might look like this:
25
26
30
20
As you can see, using the Random
class method is simple enough. Don't be afraid to introduce a bit of randomness into your programs :)
Conclusion
Java provides the Random
class to work with pseudorandom data. To work with it, we need to decide whether we need a predictable result or not. In the first case, we can use a known seed, and in the second case we can simply use the default seed which is generated based on the current system time. Remember that in Java, random sequences are only guaranteed to be the same if they are generated with the same version of Java runtime, but they can be different in different Java versions or different programming languages even for the same seed.