Package squidpony.squidmath
Class MiniMover64RNG
java.lang.Object
squidpony.squidmath.MiniMover64RNG
- All Implemented Interfaces:
Serializable
,RandomnessSource
public final class MiniMover64RNG extends Object implements RandomnessSource, Serializable
The fastest generator in this library on desktop JVMs; one of Mark Overton's subcycle generators from
this article, specifically a CMR with a 64-bit state, that has
its result multiplied by a constant. Its period is unknown, and it has multiple subcycles with different period
lengths, but one is at the very least 2 to the 42, since the generator passes PractRand after generating that many
64-bit integers (it passes 32TB with no anomalies), and all states that can be produced using
Notably, this generator's
The choice of constants for the multipliers and for the rotation needs to be carefully verified; earlier choices came close to failing PractRand at 8TB (and were worsening, so were likely to fail at 16TB), but this set of constants has higher quality in testing. Other constants did well in PractRand but had failures in TestU01 (even down to SmallCrush when reversed). For transparency, the constants used in this version:
This is a RandomnessSource but not a StatefulRandomness because it needs to take care and avoid seeds that would put it in a short-period subcycle. One long test brute-force checked all seeds from 1 to
The name comes from M. Overton, who discovered this category of subcycle generators, and also how this generator can really move when it comes to speed. This generator has less state than
Created by Tommy Ettinger on 11/26/2018.
seed(int)
have
a minimum period of 2 to the 20, or roughly one million. It also can pass TestU01's BigCrush suite in both forward
and reversed bit order, though not for all seeds (as expected).
Notably, this generator's
nextLong()
method is extremely small (as are all of the methods that use it as a
basis), which may help with inlining decisions for HotSpot. Generating the next step just needs a bitwise rotation of
the current state, multiplying the result by a 32-bit constant, and assigning that to state. Generating a long after
that only needs a multiplication by another constant, which doesn't have the issues with reversed-bits testing that
some other generators do when they multiply as their only output step (xorshift128 notably has patterns in its low
bits, so multiplying by a constant doesn't help those bits, but the CMR generator here has no such low-bit problems).
This means only three math instructions need to be performed to get a new random number (bitwise rotate left,
multiply, multiply), which is extremely low for a high-quality generator. While the CMR state change does not
pipeline well, the ease of calculating a complete number seems to make up for it on desktop JVMs.
The choice of constants for the multipliers and for the rotation needs to be carefully verified; earlier choices came close to failing PractRand at 8TB (and were worsening, so were likely to fail at 16TB), but this set of constants has higher quality in testing. Other constants did well in PractRand but had failures in TestU01 (even down to SmallCrush when reversed). For transparency, the constants used in this version:
- The state multiplier is 0xAC564B05L (or 2891336453L), which is one of the constants evaluated in Tables of Linear Congruential Generators of Different Sizes and Good Lattice Structure, by Pierre L'Ecuyer, to be optimal for an LCG with a modulus of 2 to the 32 and an odd addend (this doesn't have an addend, but it isn't an LCG either).
- The post-processing multiplier is 0x818102004182A025L (or -9115001970698837979L), which is a probable prime with a low Hamming weight (14 bits of 64 are set), in the hope it will perform well speed-wise. This number doesn't seem as critical to the quality of the generator, and some other multipliers probably work just as well.
- A left rotation constant of 29, which was chosen because it seems to allow the generator to pass certain TestU01 statistical tests, such as Birthday Spacings, where most other rotations do not.
This is a RandomnessSource but not a StatefulRandomness because it needs to take care and avoid seeds that would put it in a short-period subcycle. One long test brute-force checked all seeds from 1 to
Math.pow(2, 25)
and
validated that each of their periods is at least Math.pow(2, 20) - 1
. This means that as long as a period
as low as 1 million is rarely allowed, a starting state can be quickly chosen from a 32-bit int by using the bottom
25 bits of the int (plus 1, to disallow the 0 state) and using the remaining 7 bits to step up to 127 times through
the generator.
The name comes from M. Overton, who discovered this category of subcycle generators, and also how this generator can really move when it comes to speed. This generator has less state than
Mover64RNG
, has a shorter period than
it, and is faster than it in all aspects.
Created by Tommy Ettinger on 11/26/2018.
- Author:
- Mark Overton, Tommy Ettinger
- See Also:
- Serialized Form
-
Constructor Summary
Constructors Constructor Description MiniMover64RNG()
Callsseed(int)
with a random int value (obtained usingMath.random()
).MiniMover64RNG(int state)
The recommended constructor, this guarantees the generator will have a period of at least 2 to the 20 (roughly one million, but most if not all initial states will have much longer periods).MiniMover64RNG(long state)
Not advised for external use; preferMiniMover64RNG(int)
because it guarantees a good subcycle. -
Method Summary
Modifier and Type Method Description MiniMover64RNG
copy()
Produces a copy of this MiniMover64RNG 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
getState()
Gets the state; if this generator was set withMiniMover64RNG()
,MiniMover64RNG(int)
, orseed(int)
, then this will be on a good subcycle, otherwise it may not be.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.double
nextDouble()
Gets a pseudo-random double between 0.0 (inclusive) and 1.0 (exclusive).float
nextFloat()
Gets a pseudo-random float between 0.0f (inclusive) and 1.0f (exclusive).int
nextInt()
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
seed(int s)
Seeds the state using all bits of the given ints
.void
setState(long state)
Sets the state to any long, which may put the generator in a low-period subcycle.String
toString()
-
Constructor Details
-
MiniMover64RNG
public MiniMover64RNG()Callsseed(int)
with a random int value (obtained usingMath.random()
). -
MiniMover64RNG
The recommended constructor, this guarantees the generator will have a period of at least 2 to the 20 (roughly one million, but most if not all initial states will have much longer periods). All ints are permissible values forstate
.- Parameters:
state
- any int; will be used to get the actual state used in the generator (which is a long internally)
-
MiniMover64RNG
Not advised for external use; preferMiniMover64RNG(int)
because it guarantees a good subcycle. This constructor allows all subcycles to be produced, including ones with a shorter period.- Parameters:
state
- the state to use, exactly unless it is 0 (then this uses 1)
-
-
Method Details
-
seed
Seeds the state using all bits of the given ints
. Between 33554432 and 4294967296 seeds are possible, with the actual count probably much closer to 4294967296. This treats the bottom 25 bits ofs
(plus 1, to avoid a seed of 0) as the starting point and then generates the next state at most 127 times, with each generated state taking less time thannextLong()
. Some of the starting states are entirely possible to be within 127 steps of another starting state, so not all seeds are necessarily unique. This is not guaranteed to put the generator on an optimal subcycle, but it is guaranteed that any subcycle will have a period of at least 1048575.- Parameters:
s
- all bits are used, none verbatim (0 is tolerated)
-
nextInt
-
next
Description copied from interface:RandomnessSource
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
Description copied from interface:RandomnessSource
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)
-
nextDouble
Gets a pseudo-random double between 0.0 (inclusive) and 1.0 (exclusive).- Returns:
- a pseudo-random double between 0.0 (inclusive) and 1.0 (exclusive)
-
nextFloat
Gets a pseudo-random float between 0.0f (inclusive) and 1.0f (exclusive).- Returns:
- a pseudo-random float between 0.0f (inclusive) and 1.0f (exclusive)
-
copy
Produces a copy of this MiniMover64RNG 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 MiniMover64RNG
-
getState
Gets the state; if this generator was set withMiniMover64RNG()
,MiniMover64RNG(int)
, orseed(int)
, then this will be on a good subcycle, otherwise it may not be.- Returns:
- the state, a long
-
setState
Sets the state to any long, which may put the generator in a low-period subcycle. Useseed(int)
to guarantee a good subcycle.- Parameters:
state
- any int
-
toString
-
equals
-
hashCode
-