Package squidpony.squidmath
Class TangleRNG
java.lang.Object
squidpony.squidmath.TangleRNG
- All Implemented Interfaces:
Serializable
,RandomnessSource
,SkippingRandomness
public final class TangleRNG extends Object implements RandomnessSource, SkippingRandomness, Serializable
A very fast generator on 64-bit systems that allows choosing any of 2 to the 63 odd-number streams. Each stream
changes the set of possible outputs this can produce (amending the main flaw of ThrustAltRNG). This does well in
PractRand quality tests, passing 32TB with one minor anomaly (it shares a lot of structure with ThrustAltRNG,
which does very well in PractRand's testing as well as TestU01's BigCrush). It also outperforms just about everything
in BumbleBench benchmarks, making it arguably the fastest random number generator algorithm here that
can produce all long values (it just needs multiple generator objects to do so, all seeded differently).
Because this can produce multiple occurrences of any number in its sequence (except 0, which it should always produce once over its period of 2 to the 64), it can be considered as passing the "birthday problem" test; after running this test provided by Melissa E. O'Neill on Tangle, it correctly has 9 repeats compared to an expected 10, using the Skipping adapter to check one out of every 65536 outputs for duplicates. A generator that failed that test would have 0 repeats or more than 20, so Tangle passes. ThrustAltRNG probably also passes (or its structure allows it to potentially do so), but LightRNG, LinnormRNG, MizuchiRNG, and even ThrustRNG will fail it by never repeating an output. Truncating the output bits of any of these generators will allow them to pass this test, at the cost of reducing the size of the output to an int instead of a long (less than ideal). Notably, an individual Tangle generator tends to be able to produce about 2/3 of all possible long outputs, with roughly 1/3 of all outputs not possible to produce and another 1/3 produced exactly once. These are approximations and will vary between instances. The test that gave the "roughly 2/3 possible" result gave similar results with 8-bit stateA and stateB and with 16-bit stateA and stateB, though it could change with the 64-bit states Tangle actually uses.
The name "Tangle" comes from how the two states of this generator are "tied up in each other," with synchronized periods of 2 to the 64 (stateA) and 2 to the 63 (stateB) that repeat as a whole every 2 to the 64 outputs. Contrary to the name, Tangle isn't slowed down at all by this, but the period length of the generator is less than the maximum possible (which OrbitRNG has, though that one is slowed down).
See also
This added a small extra step to the generation on March 4, 2020; the extra step reduces correlation between streams by further randomizing the output. Without this step (an additional right-xorshift by 6), nearby stateA or stateB values would often produce visibly similar sequences; with the step they are much less similar and more random. The additional step slows down TangleRNG by a very small amount. This edit also added
Created by Tommy Ettinger on 7/9/2018.
Because this can produce multiple occurrences of any number in its sequence (except 0, which it should always produce once over its period of 2 to the 64), it can be considered as passing the "birthday problem" test; after running this test provided by Melissa E. O'Neill on Tangle, it correctly has 9 repeats compared to an expected 10, using the Skipping adapter to check one out of every 65536 outputs for duplicates. A generator that failed that test would have 0 repeats or more than 20, so Tangle passes. ThrustAltRNG probably also passes (or its structure allows it to potentially do so), but LightRNG, LinnormRNG, MizuchiRNG, and even ThrustRNG will fail it by never repeating an output. Truncating the output bits of any of these generators will allow them to pass this test, at the cost of reducing the size of the output to an int instead of a long (less than ideal). Notably, an individual Tangle generator tends to be able to produce about 2/3 of all possible long outputs, with roughly 1/3 of all outputs not possible to produce and another 1/3 produced exactly once. These are approximations and will vary between instances. The test that gave the "roughly 2/3 possible" result gave similar results with 8-bit stateA and stateB and with 16-bit stateA and stateB, though it could change with the 64-bit states Tangle actually uses.
The name "Tangle" comes from how the two states of this generator are "tied up in each other," with synchronized periods of 2 to the 64 (stateA) and 2 to the 63 (stateB) that repeat as a whole every 2 to the 64 outputs. Contrary to the name, Tangle isn't slowed down at all by this, but the period length of the generator is less than the maximum possible (which OrbitRNG has, though that one is slowed down).
See also
OrbitRNG
, which gives up more speed but moves through all 2 to the 64 long values as streams over
its full period, which is 2 to the 128 (with one stream) instead of the 2 to the 64 (with 2 to the 63 streams) here.
There's also SilkRNG
, which is like OrbitRNG but uses 32-bit math and is GWT-optimized.
This added a small extra step to the generation on March 4, 2020; the extra step reduces correlation between streams by further randomizing the output. Without this step (an additional right-xorshift by 6), nearby stateA or stateB values would often produce visibly similar sequences; with the step they are much less similar and more random. The additional step slows down TangleRNG by a very small amount. This edit also added
getStream()
, which may
have a use somewhere (maybe diagnosing possible problems?).
Created by Tommy Ettinger on 7/9/2018.
- See Also:
- Serialized Form
-
Constructor Summary
-
Method Summary
Modifier and Type Method Description TangleRNG
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.boolean
equals(Object o)
long
getStateA()
Get the "A" part of the internal state as a long.long
getStateB()
Get the "B" part of the internal state as a long.long
getStream()
Gets a long that identifies which stream of numbers this generator is producing; this stream identifier is always an odd long and won't change by generating numbers.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
setStateA(long stateA)
Set the "A" part of the internal state with a long.void
setStateB(long stateB)
Set the "B" part of the internal state with a long; the least significant bit is ignored (will always be odd).long
skip(long advance)
Advances or rolls back the SkippingRandomness' state without actually generating each number.String
toString()
-
Constructor Details
-
Method Details
-
getStateA
Get the "A" part of the internal state as a long.- Returns:
- the current internal "A" state of this object.
-
setStateA
Set the "A" part of the internal state with a long.- Parameters:
stateA
- a 64-bit long
-
getStateB
Get the "B" part of the internal state as a long.- Returns:
- the current internal "B" state of this object.
-
setStateB
Set the "B" part of the internal state with a long; the least significant bit is ignored (will always be odd).- Parameters:
stateB
- 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)
-
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
- Returns:
- a copy of this RandomnessSource
-
toString
-
equals
-
hashCode
-
skip
Advances or rolls back the SkippingRandomness' 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 tonextLong()
, and returns the random number produced at that step. Negative numbers can be used to step backward, or 0 can be given to get the most-recently-generated long fromnextLong()
.- 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
-
getStream
Gets a long that identifies which stream of numbers this generator is producing; this stream identifier is always an odd long and won't change by generating numbers. It is determined at construction and will usually (not always) change ifsetStateA(long)
orsetStateB(long)
are called. Each stream is a probably-unique sequence of 2 to the 64 longs, where approximately 1/3 of all possible longs will not ever occur (while others occur twice or more), but this set of results is different for every stream. There are 2 to the 63 possible streams, one for every odd long.- Returns:
- an odd long that identifies which stream this TangleRNG is generating from
-