Package squidpony.squidmath
Class ThrustAltRNG
java.lang.Object
squidpony.squidmath.ThrustAltRNG
- All Implemented Interfaces:
Serializable
,RandomnessSource
,SkippingRandomness
,StatefulRandomness
public final class ThrustAltRNG extends Object implements StatefulRandomness, SkippingRandomness, Serializable
A random number generator that is extremely fast but can't return all possible results. ThrustAltRNG passes TestU01's
BigCrush, which is a difficult statistical quality test. On gjrand's
"testunif" checks, this does very well on 100GB of tested data, with the "Overall summary one sided P-value P =
0.981", where 1 is perfect and 0.1 or less is a failure. On PractRand,
this passes all 32TB of generated numbers without finding any failures (and very rarely finding anomalies). Like
Because this generator can't produce all longs (it is not equidistributed), that alone is enough to discount its use in some (mainly scientific) scenarios, although it passes all major testing suites (TestU01's BigCrush, PractRand over the full 32TB of tests, and gjrand to some degree, at least better than most). DiverRNG is the default generator after ThrustAltRNG was used extensively for some time, since DiverRNG passes the same tests, is almost as fast, and is known to produce all longs over the course of its period. One small change was required to pass a test added in PractRand version 0.94, going from a shift of 22 to a shift of 23 at the end of the generation. Without this change, ThrustAltRNG will eventually fail PractRand (failing "TMFn" tests, which check for patterns like those in a linear congruential generator such as
There was a ThrustRNG in SquidLib, but it failed statistical tests badly in roughly a minute of testing, so even though it was faster it probably wasn't a good idea to use it. ThrustAltRNG modifies ThrustRNG's algorithm very heavily, and isn't especially similar, but the name stuck, I guess. The idea behind the name is that the generator is acting like a thrust in fencing, pushing quickly but leaving a hole (not in the quality, but in the distribution).
Created by Tommy Ettinger on 10/18/2017.
LightRNG
, this changes its state with a steady fixed increment, and does hash-like adjustments to the
current state to randomize it (the change is not a cipher because it is not reversible; this may be an advantage
for some usage). The period on ThrustAltRNG is 2 to the 64. ThrustAltRNG is very strong on speed, outpacing the
default generator for RNG
, DiverRNG
, by a small margin, and most other RandomnessSources in
SquidLib by a larger margin (it is slower than MiniMover64RNG
, but this has a better period). Similarly to
other hash-like PRNGs such as LightRNG
, ThrustAltRNG has a determine(long)
method that takes a state
as a long and returns a deterministic random number (each input has one output, though in this case the reverse isn't
true and some outputs will be returned by multiple inputs). Like LightRNG, but unlike an LCG such as
Random
, changing the seed even slightly generally produces completely different results, which
applies primarily to determine() but also the first number generated in a series of nextLong() calls. This generator
is GWT-safe but will be much slower on GWT than generators designed for usage there, such as GWTRNG
or
Lathe32RNG
.
Because this generator can't produce all longs (it is not equidistributed), that alone is enough to discount its use in some (mainly scientific) scenarios, although it passes all major testing suites (TestU01's BigCrush, PractRand over the full 32TB of tests, and gjrand to some degree, at least better than most). DiverRNG is the default generator after ThrustAltRNG was used extensively for some time, since DiverRNG passes the same tests, is almost as fast, and is known to produce all longs over the course of its period. One small change was required to pass a test added in PractRand version 0.94, going from a shift of 22 to a shift of 23 at the end of the generation. Without this change, ThrustAltRNG will eventually fail PractRand (failing "TMFn" tests, which check for patterns like those in a linear congruential generator such as
Random
), but only after 16TB of data has been analyzed. Even
if using a version before the shift was changed on June 8, 2019, the quality is probably fine.
There was a ThrustRNG in SquidLib, but it failed statistical tests badly in roughly a minute of testing, so even though it was faster it probably wasn't a good idea to use it. ThrustAltRNG modifies ThrustRNG's algorithm very heavily, and isn't especially similar, but the name stuck, I guess. The idea behind the name is that the generator is acting like a thrust in fencing, pushing quickly but leaving a hole (not in the quality, but in the distribution).
Created by Tommy Ettinger on 10/18/2017.
- See Also:
- Serialized Form
-
Field Summary
Fields Modifier and Type Field Description long
state
Can be any long value. -
Constructor Summary
Constructors Constructor Description ThrustAltRNG()
Creates a new generator seeded using Math.random.ThrustAltRNG(long seed)
-
Method Summary
Modifier and Type Method Description ThrustAltRNG
copy()
Produces a copy of this RandomnessSource that, if next() and/or nextLong() are called on this object and the copy, both will generate the same sequence of random numbers from the point copy() was called.static long
determine(long state)
Returns a random permutation of state; if state is the same on two calls to this, this will return the same number.static int
determineBounded(long state, int bound)
Given a state that should usually change each time this is called, and a bound that limits the result to some (typically fairly small) int, produces a pseudo-random int between 0 and bound (exclusive).static int
determineBoundedShort(int state, int bound)
Given an int state that should usually change each time this is called, and a bound that limits the result to some (typically fairly small) int, produces a pseudo-random int between 0 and bound (exclusive).static double
determineDouble(long state)
Returns a random double that is deterministic based on state; if state is the same on two calls to this, this will return the same float.static float
determineFloat(long state)
Returns a random float that is deterministic based on state; if state is the same on two calls to this, this will return the same float.static float
determineFloatFromInt(int state)
Returns a random float that is deterministic based on an int state; if state is the same on two calls to this, this will return the same float.static int
determineInt(int state)
Returns a random permutation of state; if state is the same on two calls to this, this will return the same number.boolean
equals(Object o)
long
getState()
Get the current internal state of the StatefulRandomness as a long.int
hashCode()
int
next(int bits)
Using this method, any algorithm that might use the built-in Java Random can interface with this randomness source.long
nextLong()
Using this method, any algorithm that needs to efficiently generate more than 32 bits of random data can interface with this randomness source.void
setState(long state)
Set the current internal state of this StatefulRandomness with a long.long
skip(long advance)
Advances or rolls back the ThrustAltRNG's state without actually generating each number.String
toString()
-
Field Details
-
state
Can be any long value.
-
-
Constructor Details
-
ThrustAltRNG
public ThrustAltRNG()Creates a new generator seeded using Math.random. -
ThrustAltRNG
-
-
Method Details
-
getState
Get the current internal state of the StatefulRandomness as a long.- Specified by:
getState
in interfaceStatefulRandomness
- Returns:
- the current internal state of this object.
-
setState
Set the current internal state of this StatefulRandomness with a long.- Specified by:
setState
in interfaceStatefulRandomness
- Parameters:
state
- a 64-bit long
-
next
Using this method, any algorithm that might use the built-in Java Random can interface with this randomness source.- Specified by:
next
in interfaceRandomnessSource
- Parameters:
bits
- the number of bits to be returned- Returns:
- the integer containing the appropriate number of bits
-
nextLong
Using this method, any algorithm that needs to efficiently generate more than 32 bits of random data can interface with this randomness source.Get a random long between Long.MIN_VALUE and Long.MAX_VALUE (both inclusive).
- Specified by:
nextLong
in interfaceRandomnessSource
- Returns:
- a random long between Long.MIN_VALUE and Long.MAX_VALUE (both inclusive)
-
skip
Advances or rolls back the ThrustAltRNG's state without actually generating each number. Skips forward or backward a number of steps specified by advance, where a step is equal to one call to nextLong(), and returns the random number produced at that step (you can get the state withgetState()
).- Specified by:
skip
in interfaceSkippingRandomness
- Parameters:
advance
- Number of future generations to skip over; can be negative to backtrack, 0 gets the most-recently-generated number- Returns:
- the random long generated after skipping forward or backwards by
advance
numbers
-
copy
Produces a copy of this RandomnessSource that, if next() and/or nextLong() are called on this object and the copy, both will generate the same sequence of random numbers from the point copy() was called. This just need to copy the state so it isn't shared, usually, and produce a new value with the same exact state.- Specified by:
copy
in interfaceRandomnessSource
- Specified by:
copy
in interfaceStatefulRandomness
- Returns:
- a copy of this RandomnessSource
-
toString
-
equals
-
hashCode
-
determine
Returns a random permutation of state; if state is the same on two calls to this, this will return the same number. This is expected to be called with some changing variable, e.g.determine(++state)
, where the increment for state should be odd but otherwise doesn't really matter. This multiplies state by0x6C8E9CF570932BD5L
within this method, so using a small increment won't be much different from using a very large one, as long as it is odd. The period is 2 to the 64 if you increment or decrement by 1.- Parameters:
state
- a variable that should be different every time you want a different random result; usingdetermine(++state)
is recommended to go forwards ordetermine(--state)
to generate numbers in reverse order- Returns:
- a pseudo-random permutation of state
-
determineFloat
Returns a random float that is deterministic based on state; if state is the same on two calls to this, this will return the same float. This is expected to be called with a changing variable, e.g.determine(++state)
, where the increment for state should be odd but otherwise doesn't really matter. This multiplies state by0x6C8E9CF570932BD5L
within this method, so using a small increment won't be much different from using a very large one, as long as it is odd. The period is 2 to the 64 if you increment or decrement by 1, but there are only 2 to the 30 possible floats between 0 and 1.- Parameters:
state
- a variable that should be different every time you want a different random result; usingdetermine(++state)
is recommended to go forwards ordetermine(--state)
to generate numbers in reverse order- Returns:
- a pseudo-random float between 0f (inclusive) and 1f (exclusive), determined by
state
-
determineDouble
Returns a random double that is deterministic based on state; if state is the same on two calls to this, this will return the same float. This is expected to be called with a changing variable, e.g.determine(++state)
, where the increment for state should be odd but otherwise doesn't really matter. This multiplies state by0x6C8E9CF570932BD5L
within this method, so using a small increment won't be much different from using a very large one, as long as it is odd. The period is 2 to the 64 if you increment or decrement by 1, but there are only 2 to the 62 possible doubles between 0 and 1.- Parameters:
state
- a variable that should be different every time you want a different random result; usingdetermine(++state)
is recommended to go forwards ordetermine(--state)
to generate numbers in reverse order- Returns:
- a pseudo-random double between 0.0 (inclusive) and 1.0 (exclusive), determined by
state
-
determineBounded
Given a state that should usually change each time this is called, and a bound that limits the result to some (typically fairly small) int, produces a pseudo-random int between 0 and bound (exclusive). The bound can be negative, which will cause this to produce 0 or a negative int; otherwise this produces 0 or a positive int. The state should change each time this is called, generally by incrementing by an odd number (not an even number, especially not 0). It's fine to usedetermineBounded(++state, bound)
to get a different int each time. The period is usually 2 to the 64 when you increment or decrement by 1, but some bounds may reduce the period (in the extreme case, a bound of 1 would force only 0 to be generated, so that would make the period 1).- Parameters:
state
- a variable that should be different every time you want a different random result; usingdetermineBounded(++state, bound)
is recommended to go forwards ordetermineBounded(--state, bound)
to generate numbers in reverse orderbound
- the outer exclusive bound for the int this produces; can be negative or positive- Returns:
- a pseudo-random int between 0 (inclusive) and bound (exclusive)
-
determineInt
Returns a random permutation of state; if state is the same on two calls to this, this will return the same number. This is expected to be called with some changing variable, e.g.determine(state = state + 1 | 0)
, where the increment for state should be odd but otherwise doesn't really matter (the| 0
is needed to force overflow to occur correctly on GWT; if you know you won't target GWT you can usedetermine(++state)
instead). This multiplies state by0x62BD5
within this method, so using a small increment won't be much different from using a very large one, as long as it is odd. The period is 2 to the 32 if you increment or decrement by 1 (and perform any bitwise operation, such as| 0
, if you might target GWT). If you use this on GWT and don't perform a bitwise operation on the new value for state, then the period will gradually shrink as precision is lost by the JavaScript double that GWT will use for state as a Java int. If you know that state will start at 0 and you call withdetermine(++state)
, then on GWT you may not have to worry at all until 2 to the 34 calls have been made, after which state may cease to have the precision to represent an increase by 1 when the math inside this method is considered. The period will have been exhausted by that point anyway (4 times), so it's more of a concern if state may start at a much higher int.
This only uses int math, and should be fast on GWT.- Parameters:
state
- a variable that should be different every time you want a different random result; usingdetermine(state = state + 1 | 0)
is recommended to go forwards ordetermine(state = state - 1 | 0)
to generate numbers in reverse order- Returns:
- a pseudo-random permutation of state
-
determineBoundedShort
Given an int state that should usually change each time this is called, and a bound that limits the result to some (typically fairly small) int, produces a pseudo-random int between 0 and bound (exclusive). The bound should be between -65536 and 65535, that is, the range of a short. It can be negative, which will cause this to produce 0 or a negative int; otherwise this produces 0 or a positive int. The state should change each time this is called, generally by incrementing by an odd number (not an even number, especially not 0). It's fine to usedetermineBounded(++state, bound)
to get a different int each time. The period is usually 2 to the 64 when you increment or decrement by 1, but some bounds may reduce the period (in the extreme case, a bound of 1 would force only 0 to be generated, so that would make the period 1).
This only uses int math, unlike other bounded determine() methods, but this requires the bound to be small. It should be very fast on GWT.- Parameters:
state
- an int variable that should be different every time you want a different random result; usingdetermineBounded(++state, bound)
is recommended to go forwards ordetermineBounded(--state, bound)
to generate numbers in reverse orderbound
- the outer exclusive bound for the int this produces; should be between -65536 and 65535, inclusive- Returns:
- a pseudo-random int between 0 (inclusive) and bound (exclusive)
-
determineFloatFromInt
Returns a random float that is deterministic based on an int state; if state is the same on two calls to this, this will return the same float. This is expected to be called with a changing variable, e.g.determine(++state)
, where the increment for state should be odd but otherwise doesn't really matter. This multiplies state by0x62BD5
within this method, so using a small increment won't be much different from using a very large one, as long as it is odd. The period is 2 to the 32 if you increment or decrement by 1, but there are only 2 to the 30 possible floats between 0 and 1, and this can generate less than 2 to the 24 of them.
Except for the final conversion to float, this only uses int math, and should be fast on GWT.- Parameters:
state
- an int variable that should be different every time you want a different random result; usingdetermine(++state)
is recommended to go forwards ordetermine(--state)
to generate numbers in reverse order- Returns:
- a pseudo-random float between 0f (inclusive) and 1f (exclusive), determined by
state
-