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.
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, that 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 pseudorandom values of different types, such as int, long, double, and even boolean. We will consider how to 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 theinttype;int nextInt(int n)returns a pseudorandom value ofinttype in the range from0(inclusive) ton(exclusive);long nextLong()returns a pseudorandom value oflongtype;double nextDouble()returns a pseudorandom value ofdoubletype between0.0and1.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.
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. It allows us 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 (2) as we need.
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, we have to generate numbers exactly within the range from 20 to 30 (inclusive):
20 30
An output example:
25
26
30
20
As you can see, dealing with the Random class 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 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.