Summary

Provide new interface types and implementations for pseudorandom number generators (PRNGs), including jumpable PRNGs and an additional class of splittable PRNG algorithms (LXM).

Goals

Make it easier to use various PRNG algorithms interchangeably in applications.

Better support stream-based programming by providing streams of PRNG objects.

Eliminate code duplication in existing PRNG classes.

Make it easier for third-party developers to create new full-featured PRNGs.

Carefully preserve existing behavior of class java.util.Random .

Non-Goals

It is not a goal to provide implementations of numerous other PRNG algorithms, only to provide a framework that can accommodate numerous other PRNG algorithms if others wish to code them. (However, we do suggest providing four common algorithms that have already been widely deployed in other programming language environments.)

Success Metrics

The output of the new LXM algorithms should pass the existing well-known TestU01 and PractRand test suites.

Pierre L'Ecuyer and Richard Simard. TestU01: A C Library for Empirical Testing of Random Number Generators. ACM Transactions on Mathematical Software 33, 4 (August 2007), article 22. ISSN 0098-3500. http://doi.acm.org/10.1145/1268776.1268777

Richard Simard. TestU01 version 1.2.3 (August 2009). http://www.iro.umontreal.ca/~simardr/testu01/tu01.html

Pierre L'Ecuyer and Richard Simard. TestU01: A Software Library in ANSI C for Empirical Testing of Random Number Generators: User's guide, compact version. Département d'Informatique et de Recherche Opérationnelle, Univerité de Montréal, May 2013. http://www.iro.umontreal.ca/~simardr/testu01/guideshorttestu01.pdf

Chris Doty-Humphrey. PractRand version 0.90. July 2014. http://pracrand.sourceforge.net [That's not a typo. The name of the software is "PractRand" but the SourceForge project name is "pracrand".]

Jumpable and leapable PRNG algorithms should pass tests (TBD) that verify the commutativity of certain operations.

Motivation

We see five opportunities for improvement in the area of pseudorandom number generators in Java:

With the existing (JDK 8) PRNG classes Random , ThreadLocalRandom , and SplittableRandom , it is difficult to replace any one of them in an application with some other algorithm, despite the fact that they all support pretty much the same set of methods. For example, if an application uses instances of class Random , it will necessarily declare variables of type Random , which cannot hold instances of class SplittableRandom ; changing the application to use SplittableRandom would require changing the type of every variable (including method parameters) used to hold a PRNG object. The one exception is that ThreadLocalRandom is a subclass of Random , purely to allow variables of type Random to hold instances of ThreadLocalRandom , yet ThreadLocalRandom overrides nearly all the methods of Random . Interfaces can easily address this.

Classes Random , ThreadLocalRandom , and SplittableRandom all support such methods as nextDouble() and nextBoolean() as well as stream-producing methods such as ints() and longs() , but they have completely independent and nearly copy-and-paste identical implementations. Refactoring this code would make it easier to maintain and, moreover, if properly documented would make it much easier for third parties to create new PRNG classes that also support the same complete suite of methods.

In 2016, testing revealed two new weaknesses in the algorithm used by class SplittableRandom . On the one hand, a relatively minor revision can avoid those weaknesses. On the other hand, a new class of splittable PRNG algorithms (LXM) has also been discovered that are almost as fast, even easier to implement, and appear to completely avoid the three classes of weakness to which SplittableRandom is prone.

Being able to obtain a stream of PRNG objects from a PRNG would make it much easier to express certain sorts of code using streaming methods.

There are many PRNG algorithms in the literature that are not splittable but are jumpable (and perhaps also leapable, that is, capable of very long jumps as well as ordinary jumps), a property quite different from splitting that nevertheless also lends itself to supporting streams of PRNG objects. Currently it is difficult to take advantage of this property in Java. Examples of jumpable PRNG algorithms are MRG32k3a, Xoshiro256**, and Xoroshiro128+. MRG32k3a: Gregory W. Fischer, Ziv Carmon, Dan Ariely, Gal Zauberman, and Pierre L'Ecuyer. Good Parameters and Implementations for Combined Multiple Recursive Random Number Generators. Operations Research 47, 1 (January 1999), 159-164. http://dx.doi.org/10.1287/opre.47.1.159 Xoshiro256** and Xoroshiro128+: http://xoshiro.di.unimi.it



Description

We propose to create five new interfaces: Rng , SplittableRng , JumpableRng , LeapableRng , and ArbitrarilyJumpableRng . Roughly speaking:

Rng would provide methods named ints , longs , doubles , nextBoolean , nextInt , nextLong , nextDouble , and nextFloat , with all their current parameter variations.

SplittableRng would extend Rng and also provide methods named split and splits .

JumpableRng would extend Rng and also provide methods named jump and jumps .

LeapableRng would extend JumpableRng and also provide methods named leap and leaps .

ArbitrarilyJumpableRng would extend LeapableRng and also provide additional variations of jump and jumps that allow an arbitrary jump distance to be specified.

We propose to refactor Random , ThreadLocalRandom , and SplittableRandom so as to share most of their implementation code and, furthermore, make that code reusable by other algorithms as well. This refactoring would create new public abstract classes AbstractRng , AbstractSplittableRng , AbstractJumpableRng , AbstractLeapableRng , and AbstractArbitrarilyJumpableRng , each of which requires an extending class to provide only implementations for methods nextInt() , nextLong() , and (if relevant) either split() , or jump() , or jump() and leap() , or jump(distance) . After this refactoring, Random , ThreadLocalRandom , and SplittableRandom would all implement the Rng interface. Note that because SecureRandom is a subclass of Random , all instances of SecureRandom would also automatically support the Rng interface, with no need to recode the SecureRandom class or any of its associated implementation engines.

We propose to add new classes that extend AbstractSplittableRng (and therefore implement SplittableRng and Rng ) to support six specific members of the LXM family of PRNG algorithms (the current prototype versions require less than 50 lines of code each):

L32X64Random

L32X64MixRandom

L64X128Random

L64X128MixRandom

L64X256Random

L64X256MixRandom

We also propose to provide implementations of these widely-used PRNG algorithms:

MRG32K3A

Xoshiro256StarStar

Xoroshiro128StarStar

Xoroshiro128Plus

This suite of ten algorithms would provide Java programmers with a reasonable range of tradeoffs among space, time, quality, and compatibility with other languages.

Alternatives

We considered simply introducing new interfaces while leaving the implementations of Random , ThreadLocalRandom , and SplittableRandom as is. This would help to make PRNG objects more easily interchangeable but would not make it any easier to implement them.

We considered refactoring Random , ThreadLocalRandom , and SplittableRandom without changing their functionality or adding any new interfaces. We believe this would reduce their overall memory footprint, but do nothing to make future PRNG algorithms easier to implement or use.

Testing

All existing tests for Random , ThreadLocalRandom , and SplittableRandom should continue to be used.

New test, probably to be applied just once: The output of the refactored versions of Random , ThreadLocalRandom , and SplittableRandom (before repairing the two newly detected weaknesses) should be spot-checked against the existing (JDK 8) implementations to verify that their behavior remains unchanged.

New test, probably to be applied just once: The output of the LXM algorithms should be spot-checked against the C-coded versions used for quality verification with TestU01 and PractRand.

New test, to become a permanent part of the test suite: The jump() and leap() methods should be tested to verify that they do travel around the state cycle by the claimed distance. For example, starting from any specific initial state, the sequence of operations nextLong(); jump() ought to leave a generator in the same state as the sequence of operations jump(); nextLong() .

Risks and Assumptions

We believe this is a fairly small project and the risks are minimal. Probably the biggest burden is crafting the specification; the second-biggest is testing. The refactoring of existing algorithms and the coding of new algorithms has already been completed.