Seeded RandomGenerators in Java 17
Asked Answered
M

1

14

Java 17 has introduced a new RandomGeneratorFactory class for instantiating random number generators. The create(long) method is described as

Create an instance of RandomGenerator based on algorithm chosen providing a starting long seed. If long seed is not supported by an algorithm then the no argument form of create is used.

However there doesn't seem to be a method of RandomGeneratorFactory that can be used to determine if a long seed is supported. How is it possible to use the new API to obtain an instance of RandomGenerator that is guaranteed to be created using a supplied long seed?

Mulberry answered 31/7, 2022 at 16:29 Comment(6)
I'm fairly sure the answer is that you can't. The API also doesn't allow you to create efficient, thread-safe implementations (#73021720) - i.e. ThreadLocalRandom is now considered "legacy", but no suitable replacement has been added for the problem ThreadLocalRandom was introduced to solve. I'm really struggling to understand how this class was considered good enough for inclusion in the JDK.Mulberry
Yes I've read the source code too, but that's an implementation detail, not part of the public API. i.e. that approach could break in the next version of Java. What I really want to understand is why there isn't a isLongSeedSupported method on the interface.Mulberry
Some methods of RandomGeneratorFactory hint whether a seed would be effective. I’m not certain, but I think if isHardware or isStochastic returns true, a seed is not used. I think if isStatistical returns true or period() returns a nonzero value, the instance likely uses a seed.Aparri
Those rules certainly seem to work for the 13 factories I'm getting from RandomGeneratorFactory.all(). The only one that doesn't support a seed is SecureRandom and the other 12 have isHardware=false, isStochastic=false, isStatistical=true and period>0. Such a bizarre API though.Mulberry
Look at SplittableRandom, which is designed to go hand in hand with parallel decomposition.Strainer
Thanks, that does help as I can use the constructor of SplittableRandom with a long seed (which I wasn't aware of before). Although, as SplittableRandom is also now described as "legacy" (docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/…), it does feel a bit odd that it's still necessary.Mulberry
L
1

Probably, this is not the answer you want, but I became curious after reading your question and thought: Why not just probe the generator in question? Hence, I created a little scratch file with a heuristic approach:

import java.util.List;
import java.util.random.RandomGenerator;
import java.util.random.RandomGeneratorFactory;

import static java.util.stream.Collectors.toList;

class RandomGeneratorTool_73185029 {
  public static void main(String[] args) {
    RandomGeneratorFactory.all()
      .map(factory -> String.format("%-21s -> %sseedable", factory.name(), isSeedable(factory) ? "" : "not "))
      .forEach(System.out::println);
  }

  public static boolean isSeedable(RandomGeneratorFactory<RandomGenerator> factory) {
    final int ROUNDS = 3, NUMBERS_PER_ROUND = 100, SEED = 123;
    List<Integer>[] randomNumbers = new List[ROUNDS];
    for (int round = 0; round < ROUNDS; round++) {
      randomNumbers[round] = factory.create(SEED).ints(NUMBERS_PER_ROUND).boxed().collect(toList());
      if (round > 0 && !randomNumbers[round - 1].equals(randomNumbers[round]))
        return false;
    }
    return true;
  }
}

On JDK 21, this prints:

L32X64MixRandom       -> seedable
L128X128MixRandom     -> seedable
L64X128MixRandom      -> seedable
SecureRandom          -> not seedable
L128X1024MixRandom    -> seedable
L64X128StarStarRandom -> seedable
Xoshiro256PlusPlus    -> seedable
L64X256MixRandom      -> seedable
Random                -> seedable
Xoroshiro128PlusPlus  -> seedable
L128X256MixRandom     -> seedable
SplittableRandom      -> seedable
L64X1024MixRandom     -> seedable
Luckless answered 25/10, 2023 at 1:43 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.