Better Random Number Generators

Quick summary:

  • Revamps the family hierarchy of pseudo-random number generators (PRNGs) by providing new interfaces (for better loose coupling and polymorphism) and implementations that offer better randomisation algorithms.
  • Released in Java 17 (2021).

Key Features

  • Better API Design: By grouping everything under the RandomGenerator-interface, we have a uniform API allowing for better loose coupling and polymorphism, meaning switching implementations is easy, and some handy new features.
  • Better Algorithms: Access to different implementations and algorithms means better quality of randomness, which is important for cryptographic security, data analysis and realistic simulations.
  • Streams: It’s 2021, it’s about time we have an easy way to create streams of pseudo-random numbers directly, simplifying our code massively for complex operations.

Example

Before Java 17:

Previously, we only had classes (not interfaces), so switching between implementations was a pain.

// Before Java 17
Random random = new Random();
System.out.println(random.nextInt(101)); // Random number between 1 and 100 (inclusive)

After Java 17:

Now we have a beautiful hierarchy of multiple implementations, including the old API classes, fit into a new design with one uniform interface, the RandomGenerator-interface.

// After Java 17
RandomGenerator randomGenerator = new Random();
System.out.println(random.nextInt(101));
// Cool new .nextInt() that takes a range
System.out.println(random.nextInt(-10, -11));

// Better algorithm
RandomGenerator betterRandomGenerator = RandomGenerator.getDefault();
System.out.println(betterRandomGenerator.nextInt(101));

New Stream Methods

MethodDescriptionExample Usage
ints()Generates an effectively infinite stream of pseudorandom int values.IntStream ints = rng.ints();
ints(long streamSize)Generates a stream of a specified number of pseudorandom int values.IntStream ints = rng.ints(100);
ints(int origin, int bound)Generates an infinite stream of pseudorandom int values between the specified origin (inclusive) and the bound (exclusive).IntStream ints = rng.ints(0, 10);
ints(long streamSize, int origin, int bound)Generates a stream of pseudorandom int values, with the size and range specified.IntStream ints = rng.ints(100, 0, 10);
longs()Generates an effectively infinite stream of pseudorandom long values.LongStream longs = rng.longs();
longs(long streamSize)Generates a stream of a specified number of pseudorandom long values.LongStream longs = rng.longs(100);
longs(long origin, long bound)Generates an infinite stream of pseudorandom long values between the specified origin (inclusive) and the bound (exclusive).LongStream longs = rng.longs(0L, 10L);
longs(long streamSize, long origin, long bound)Generates a stream of pseudorandom long values, with the size and range specified.LongStream longs = rng.longs(100, 0L, 10L);
doubles()Generates an effectively infinite stream of pseudorandom double values.DoubleStream doubles = rng.doubles();
doubles(long streamSize)Generates a stream of a specified number of pseudorandom double values.DoubleStream doubles = rng.doubles(100);
doubles(double origin, double bound)Generates an infinite stream of pseudorandom double values between the specified origin (inclusive) and the bound (exclusive).DoubleStream doubles = rng.doubles(0.0, 10.0);
doubles(long streamSize, double origin, double bound)Generates a stream of pseudorandom double values, with the size and range specified.DoubleStream doubles = rng.doubles(100, 0.0, 10.0);
For a full overview, check out the JDK 17 Docs on RandomGenerator.


πŸ‘©β€πŸ’» Hands-on Demo: Better Random Number Generators

1. Magic 8 Ball

  1. In the main-method of a Magic8Ball-class, let the user enter a question, and the program respond with an answer.
    • Use RandomGenerator as a reference type, but a Random as an object type.
    • Use RandomGenerator as a PRNG with default configuration.
    • Use the .of() factory method to select a specific PRNG algorithm such as “Xoshiro256PlusPlus” or “L128X256MixRandom“.
● It is certain
● It is decidedly so
● Without a doubt
● Yes definitely
● You may rely on it
● As I see it, yes
● Most likely
● Outlook good
● Yes
● Signs point to yes
● Reply hazy, try again
● Ask again later
● Better not tell you now
● Cannot predict now
● Concentrate and ask again
● Don’t count on it
● My reply is no
● My sources say no
● Outlook not so good
● Very doubtful

2. Weather Forecast

  1. In the main-method of a WeatherForecast-class, use RandomGenerator and its fancy new nextDouble(int origin, int bound)-method to print a 7-day forecast (average temps as shown below).
    • Temps must be between -5 and 19 (inclusive).
    • Use the .of() factory method to select a specific PRNG algorithm such as “Xoshiro256PlusPlus” or “L128X256MixRandom“.

3. Dice Rolls

  1. In the main-method of a DiceRolls-class ask the user to enter a number of dice to roll.
  2. Simulate throwing x number of 6-sided dice in one line by using RandomGenerator‘s out-of-the-box streams.

Solutions

πŸ•΅οΈβ€β™‚οΈ Click here to reveal the solutions

1. Magic 8 Ball

public class Magic8Ball {
    public static void main(String[] args) {
        Scanner keyboard = new Scanner(System.in);
        System.out.println("What is your question?");
        String question = keyboard.nextLine();

        System.out.println("The answer is: 🎱 " + generateRandomAnswer());
    }

    private static String generateRandomAnswer() {
        RandomGenerator rng = RandomGenerator.of("Xoshiro256PlusPlus");
        String[] answers = {
                "It is certain",
                "It is decidedly so",
                "Without a doubt",
                "Yes - definitely",
                "You may rely on it",
                "As I see it, yes",
                "Most likely",
                "Outlook good",
                "Yes",
                "Signs point to yes",
                "Reply hazy, try again",
                "Ask again later",
                "Better not tell you now",
                "Cannot predict now",
                "Concentrate and ask again",
                "Don't count on it",
                "My reply is no",
                "My sources say no",
                "Outlook not so good",
                "Very doubtful"
        };
        int answerIndex = rng.nextInt(answers.length);
        return answers[answerIndex];
    }

}

2. Weather Forecast

public class WeatherForecast {
    public static void main(String[] args) {
        LocalDate today = LocalDate.now();
        int nrOfDays = 7;
        for (int i = 0; i < nrOfDays; i++) {
            LocalDate date = today.plusDays(i);
            System.out.printf("%s: %.1f℃\t", date.getDayOfWeek().name(), generateRandomTemp());
        }
    }

    private static double generateRandomTemp() {
        RandomGenerator rng = RandomGenerator.of("Xoshiro256PlusPlus");
        return rng.nextDouble(-5, 20); // Between -5 and 19 degrees Celsius (inclusive)
    }
}

3. Dice Rolls

public class DiceRolls {
    public static void main(String[] args) {
        Scanner keyboard = new Scanner(System.in);
        System.out.println("How many dice do you want to roll?");
        int nrOfDice = keyboard.nextInt();
        RandomGenerator.getDefault().ints(nrOfDice, 1, 6 + 1)
                .forEach(result -> System.out.printf("🎲 Rolled a %d\n", result));
    }
}