Skip to content

Commit

Permalink
Lots of documentation, for RNGs mostly.
Browse files Browse the repository at this point in the history
  • Loading branch information
tommyettinger committed Oct 29, 2021
1 parent 15e3de4 commit ed8fc09
Show file tree
Hide file tree
Showing 9 changed files with 75 additions and 21 deletions.
2 changes: 2 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
- Added FourWheelRandom as yet another choice of EnhancedRandom; this is the fastest so far on Java 16 with HotSpot, and has four `long`s of state.
- Many convenience methods and important constructors in the deque classes, such as `with()` and copy constructors.
- EnhancedRandom now optionally provides a `previousLong()` method, which steps the generator backwards; all generators here implement it.
- There is now an equate() method that can be overridden in all maps and sets, to change how equality is compared. This replaces the usage of locateKey() for the same task.
- This release does not use Fibonacci hashing for any keys, though comments show how to implement it.

[0.1.3]
- [BREAKING CHANGE] TricycleRandom needed its algorithm changed to improve its quality for some problem seeds; performance is pretty much unaffected.
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTORS
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@
# isn't direct code contribution is in the THANKS file.
#
tommyettinger https://github.com/tommyettinger
NathanSweet https://github.com/NathanSweet
# NathanSweet should be here, but I don't want to make him sign a CLA...
28 changes: 26 additions & 2 deletions THANKS
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,38 @@ for writing the original libGDX data structures, and
adding various API improvements to the data structures
used here when they were part of libGDX. Nathan wrote a
lot of the API structure and has been very influential.
Nathan should be a contributor here, but I don't have a
CLA to sign, and after he made me sign the CLA for
libGDX, I don't feel like putting him through the same
hassle. Do unto others, etc.

Thanks to pyb1993, https://github.com/pyb1993 , for
coming up with the idea and most of the implementation
for several useful optimizations applied in version 0.2.0.

Thanks to Jon Renner, https://github.com/jrenner , for
the Select and QuickSelect code, as added to libGDX.
Jon would be a contributor but I don't have a CLA to sign.
Jon would be a contributor but I don't have a CLA to sign,
nor an inclination to ask him to go through that hassle.

Thanks to Sebastiano Vigna, https://github.com/vigna , for
fastutil with its useful sorts and primitive comparators.
fastutil with its useful sorts and primitive comparators.
Most of the com.github.tommyettinger.ds.support.sort
package is from fastutil.

Thanks again to Sebastiano Vigna and David Blackman, who
designed the Xoshiro256** generator, present here with
only the changes necessary for the Java port from their
CC0-licensed C code: https://prng.di.unimi.it/xoshiro256starstar.c

Thanks to CERN, for not destroying the Earth in a black
hole, and also for providing the sort code used by
ObjectComparators.

Thanks to Guy Steele and the rest of the team behind Java
8's SplittableRandom class, which is very similar in
structure and design to DistinctRandom here. Also some
thanks to Guy Steele in particular for providing only
"slow generators" in Java 17, leaving room for third
parties to provide "fast generators." We don't implement
Java 17 random number generator interfaces for a reason.
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@
* you can use {@link TricycleRandom} or {@link FourWheelRandom} (FourWheelRandom is preferred if you only target Java
* 16). Those generators also should have a longer period than DistinctRandom except in infinitesimally-rare cases. No
* other generators in jdkgdxds have the "distinct" quality this generator has.
* <br>
* Unlike the multiple-state generators here, DistinctRandom tolerates being given sequential seeds and/or states, and
* in fact doesn't randomize the seed when given one with {@link #setSeed(long)}. This is the only generator here that
* performs two multiplications on its output (other than {@link Xoshiro256StarStarRandom}, which doesn't do much good
* by multiplying by 5 and 9); having multiple large multiplications tends to mix bits very thoroughly as long as there
* are some xorshifts or rotations between the multiplications.
* <br>
* This implements all methods from {@link EnhancedRandom}, including the optional {@link #skip(long)} and
* {@link #previousLong()} methods.
*/
public class DistinctRandom extends Random implements EnhancedRandom {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,14 @@
* two of the other states and combine them; C rotates state B and subtracts state D, while D simply XORs states B and C.
* This returns the state D that the previous step generated. This performs better than TricycleRandom simply because each
* of the states can be updated in parallel (using ILP) and all of the updates depend on either one or two states, instead
* of one, two, or three with TricycleRandom. It implements all optional methods in EnhancedRandom except
* of one, two, or three with TricycleRandom.
* <br>
* It is strongly recommended that you seed this with {@link #setSeed(long)} instead of
* {@link #setState(long, long, long, long)}, because if you give sequential seeds to both setSeed() and setState(), the
* former will start off random, while the latter will start off repeating the seed sequence. After about 20-40 random
* numbers generated, any correlation between similarly seeded generators will probably be completely gone, though.
* <br>
* This implements all optional methods in EnhancedRandom except
* {@link #skip(long)}; it does implement {@link #previousLong()} without using skip().
*/
public class FourWheelRandom extends Random implements EnhancedRandom {
Expand Down
18 changes: 14 additions & 4 deletions src/main/java/com/github/tommyettinger/ds/support/LaserRandom.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,11 @@
* output, if you appended all 2 to the 63 possible LaserRandom streams in full, the gargantuan result would
* include all longs equally often. So, if the stream is selected effectively at random, then the subset of
* that stream that actually gets used should be fair (and it's very unlikely that any usage will need a full
* stream of over 18 quintillion pseudo-random longs).
* stream of over 18 quintillion pseudo-random longs). It is strongly recommended that you use very different
* numbers when creating many LaserRandom objects with similar states, because there is a noticeable
* correlation between, for instance, a grid of LaserRandom objects initialized with stateA drawn from the
* odd numbers 1 through 101, and stateB drawn from another odd number 1 through 101. Using
* {@link #setSeed(long)} essentially eliminates this risk, so it's a good idea to seed this with one long.
* <br>
* If statistical quality is a concern, don't use {@link Random}, since the aforementioned
* analysis finds statistical failures in about a minute when checking about 16GB of output; this class can
Expand All @@ -68,7 +72,10 @@
* implemented in C and compiled with GCC or Clang, typically). There are also some concerns about specific
* failure cases when the output of xoroshiro128** or xoshiro256** is multiplied by any of quadrillions of
* constants and tested after that multiplication (see M.E. O'Neill's dissection of xoshiro256**
* <a href="https://www.pcg-random.org/posts/a-quick-look-at-xoshiro256.html">here</a>).
* <a href="https://www.pcg-random.org/posts/a-quick-look-at-xoshiro256.html">here</a>). Xoshiro256**, like
* LaserRandom, can't be reliably initialized using nearby values for its state variables, and does much
* better if you use its {@link Xoshiro256StarStarRandom#setSeed(long)} method. We do implement Xoshiro256**
* here, because it provides 4-dimensional equidistribution, and that is hard to find.
* <br>
* You can copy this class independently of the library it's part of; it's meant as a general replacement for
* Random and also RandomXS128. LaserRandom is generally faster than RandomXS128, and can be over 3x faster
Expand All @@ -83,8 +90,11 @@
* <br>
* You may want to compare this class with TricycleRandom and FourWheelRandom in the same package; both of
* those have a larger state size (and should usually have a larger period), are usually faster, and also
* implement all of EnhancedRandom (except for {@link #skip(long)}), but they don't randomize the first
* result they return, so if the seeding has a pattern, then the start of their sequences will have patterns.
* implement all of EnhancedRandom (except for {@link #skip(long)}), but they do even less randomizing for
* the first result they return, so if the seeding has a pattern, then the start of their sequences will
* have patterns. These patterns are less obvious but do persist in LaserRandom, and don't persist in
* TricycleRandom or FourWheelRandom over a long run. All generators here do well when using
* {@link #setSeed(long)} to set the full state.
* <br>
* Pew pew! Lasers!
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@
* not easy. This generator is meant in particular to optimize well for GPU computations, even though Java doesn't have much
* ability to do this currently. Some uncommon platforms may also optimize this better than FourWheelRandom.
* <br>
* It is strongly recommended that you seed this with {@link #setSeed(long)} instead of
* {@link #setState(long, long, long, long)}, because if you give sequential seeds to both setSeed() and setState(), the
* former will start off random, while the latter will start off repeating the seed sequence. After about 20-40 random
* numbers generated, any correlation between similarly seeded generators will probably be completely gone, though.
* The setSeed() method isn't as fast here as it is in some other generators.
* <br>
* It implements all optional methods in EnhancedRandom except {@link #skip(long)} and {@link #previousLong()}.
*/
public class StrangerRandom extends Random implements EnhancedRandom {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,19 +44,13 @@
* TricycleRandom passes 64TB of testing with PractRand, which uses a suite of tests to look for a variety of potential
* problems. It has also passed a whopping 4 petabytes of testing with hwd, can test a much larger amount of data but
* only runs a single test. The test hwd uses looks for long-range bit-dependencies, where one bit's state earlier in
* the generated numbers determines the state of a future bit with a higher-than-reasonable likelihood. All three of
* this generator, {@link LaserRandom} and {@link DistinctRandom} are considered stable.
* the generated numbers determines the state of a future bit with a higher-than-reasonable likelihood. All the
* generators here are considered stable.
* <br>
* This can be used as a substitute for {@link LaserRandom}, but it doesn't start out randomizing its early results very
* well, unlike LaserRandom. If you initialize this with {@link #setSeed(long)}, then the results should be random from
* the start, and unrelated to the original seed. It can also be more of a challenge to handle 3 states than 2 in some
* situations, or 1 state for DistinctRandom. You might prefer LaserRandom's many different streams, which shouldn't
* overlap and have a guaranteed period of 2 to the 64, to TricycleRandom's big unknown sub-cycles. LaserRandom and
* DistinctRandom can also {@link LaserRandom#skip(long)} while this cannot. Another alternative is
* {@link FourWheelRandom}, which is similar to this class but has a larger state and tends to be faster on Java 16 with
* HotSpot, though slightly slower on Java 8 with HotSpot. If you don't want to use TricycleRandom because it has too
* many states or doesn't randomize its starting value enough, FourWheelRandom won't solve those either, and you should
* turn to DistinctRandom or LaserRandom.
* It is strongly recommended that you seed this with {@link #setSeed(long)} instead of
* {@link #setState(long, long, long, long)}, because if you give sequential seeds to both setSeed() and setState(), the
* former will start off random, while the latter will start off repeating the seed sequence. After about 20-40 random
* numbers generated, any correlation between similarly seeded generators will probably be completely gone, though.
*/
public class TricycleRandom extends Random implements EnhancedRandom {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
* It can be considered stable, like the other EnhancedRandom implementations here. This passes heavy testing, but isn't a
* cryptographic generator, and it does have known issues when its output is multiplied by certain specific constants (any
* of a lot) and tests are then run. The only invalid state is the one with 0 in each state variable, and this won't ever
* occur in the normal period of that contains all other states.
* occur in the normal period of that contains all other states. You should generally seed this with {@link #setSeed(long)},
* rather than {@link #setState(long, long, long, long)}, because if you give similar states to the latter, it tends to
* produce severely flawed output on at least the low-order bits. This can't happen with setSeed().
* <br>
* The main reasons you could prefer this generator to the typically-faster {@link FourWheelRandom} are:
* <ul>
Expand Down

0 comments on commit ed8fc09

Please sign in to comment.