Package squidpony.squidmath
Class PermutedRNG
java.lang.Object
squidpony.squidmath.PermutedRNG
- All Implemented Interfaces:
Serializable
,RandomnessSource
,SkippingRandomness
,StatefulRandomness
public final class PermutedRNG extends Object implements RandomnessSource, StatefulRandomness, SkippingRandomness, Serializable
This is a RandomnessSource in the PCG-Random family. It performs pseudo-
random modifications to the output based on the techniques from the
Permuted Congruential Generators created by M.E. O'Neill.
Specifically, this variant is:
RXS M XS -- random xorshift, mcg multiply, fixed xorshift
The most statistically powerful generator, but all those steps
make it slower than some of the others.
Even though benchmarks on similar programs in C would lead you to believe this should be somewhat faster than LightRNG, benchmarking with JMH seems to show LightRNG being roughly 16% faster than PermutedRNG, and both drastically faster than java.util.Random . This generator was implemented incorrectly for a large part of its history, but it seems correct now, though it may be a little slower.
Even though benchmarks on similar programs in C would lead you to believe this should be somewhat faster than LightRNG, benchmarking with JMH seems to show LightRNG being roughly 16% faster than PermutedRNG, and both drastically faster than java.util.Random . This generator was implemented incorrectly for a large part of its history, but it seems correct now, though it may be a little slower.
- Author:
- Melissa E. O'Neill (Go HMC!), Tommy Ettinger
- See Also:
PintRNG is similar to this algorithm but uses only 32-bit math, where possible.
, Serialized Form
-
Field Summary
Fields Modifier and Type Field Description long
state
The state can be seeded with any value. -
Constructor Summary
Constructors Constructor Description PermutedRNG()
Creates a new generator seeded using Math.random.PermutedRNG(long seed)
Constructs a new PermutedRNG with the given seed as its state, exactly. -
Method Summary
Modifier and Type Method Description PermutedRNG
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)
Given suitably-different inputs asstate
, this will permute that state to get a seemingly-unrelated number.static int
determineBounded(long state, int bound)
Given suitably-different inputs asstate
, this will permute that state to get a seemingly-unrelated number as an int between 0 and bound.long
getState()
Gets the current state of this generator.int
next(int bits)
Gets a random int with at most the specified number of bits.boolean
nextBoolean()
Gets a random value, true or false.void
nextBytes(byte[] bytes)
Given a byte array as a parameter, this will fill the array with random bytes (modifying it in-place).double
nextDouble()
Gets a uniform random double in the range[0.0,1.0)
.double
nextDouble(double outer)
Gets a uniform random double in the range [0.0,outer) given a positive parameter outer.float
nextFloat()
Gets a uniform random float in the range[0.0,1.0)
CallsnextLong()
exactly once.int
nextInt()
Can return any int, positive or negative, of any size permissible in a 32-bit signed integer.int
nextInt(int bound)
Exclusive on the outer bound; the inner bound is 0.int
nextInt(int lower, int upper)
Inclusive lower, exclusive upper.long
nextLong()
Can return any long, positive or negative, of any size permissible in a 64-bit signed integer.long
nextLong(long bound)
Exclusive on the outer bound; the inner bound is 0.long
nextLong(long inner, long outer)
Inclusive inner, exclusive outer; both inner and outer can be positive or negative.void
setSeed(long seed)
Sets the seed of this generator (which is also the current state).void
setState(long seed)
Sets the seed (also the current state) of this generator.long
skip(long advance)
Advances or rolls back the PermutedRNG's state without actually generating each number.String
toString()
-
Field Details
-
state
The state can be seeded with any value.
-
-
Constructor Details
-
PermutedRNG
public PermutedRNG()Creates a new generator seeded using Math.random. -
PermutedRNG
Constructs a new PermutedRNG with the given seed as its state, exactly.- Parameters:
seed
- a long that will be used as-is for the state of a new PermutedRNG
-
-
Method Details
-
next
Gets a random int with at most the specified number of bits. Equivalent in its effect on the state to callingnextLong()
exactly one time.- Specified by:
next
in interfaceRandomnessSource
- Parameters:
bits
- the number of bits to be returned, between 1 and 32- Returns:
- a pseudo-random int with at most the specified bits
-
nextInt
Can return any int, positive or negative, of any size permissible in a 32-bit signed integer. Equivalent in its effect on the state to callingnextLong()
exactly one time.- Returns:
- any int, all 32 bits are random
-
nextLong
Can return any long, positive or negative, of any size permissible in a 64-bit signed integer.- Specified by:
nextLong
in interfaceRandomnessSource
- Returns:
- any long, all 64 bits are random
-
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 needs 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
-
nextInt
Exclusive on the outer bound; the inner bound is 0. CallsnextLong()
exactly once.- Parameters:
bound
- the upper bound; can be positive or negative- Returns:
- a random int less than n and at least equal to 0
-
nextInt
Inclusive lower, exclusive upper. The upper bound is really the outer bound, and the lower bound the inner bound, because upper is permitted to be less than lower, though upper is still exclusive there. CallsnextLong()
exactly once.- Parameters:
lower
- the lower bound, inclusive, can be positive or negativeupper
- the upper bound, exclusive, can be positive or negative- Returns:
- a random int at least equal to lower and less than upper (if upper is less than lower, then the result will be less than or equal to lower and greater than upper)
-
nextLong
Exclusive on the outer bound; the inner bound is 0. The bound may be negative, which will produce a non-positive result. CallsnextLong()
exactly once.- Parameters:
bound
- the outer exclusive bound; may be positive or negative- Returns:
- a random long between 0 (inclusive) and bound (exclusive)
-
nextLong
Inclusive inner, exclusive outer; both inner and outer can be positive or negative. CallsnextLong()
exactly once.- Parameters:
inner
- the inner bound, inclusive, can be positive or negativeouter
- the outer bound, exclusive, can be positive or negative and may be greater than or less than inner- Returns:
- a random long that may be equal to inner and will otherwise be between inner and outer
-
nextDouble
Gets a uniform random double in the range[0.0,1.0)
. CallsnextLong()
exactly once.- Returns:
- a random double at least equal to 0.0 and less than 1.0
-
nextDouble
Gets a uniform random double in the range [0.0,outer) given a positive parameter outer. If outer is negative, it will be the (exclusive) lower bound and 0.0 will be the (inclusive) upper bound. CallsnextLong()
exactly once.- Parameters:
outer
- the exclusive outer bound, can be negative- Returns:
- a random double between 0.0 (inclusive) and outer (exclusive)
-
nextFloat
Gets a uniform random float in the range[0.0,1.0)
CallsnextLong()
exactly once.- Returns:
- a random float at least equal to 0.0f and less than 1.0f
-
nextBoolean
Gets a random value, true or false. CallsnextLong()
exactly once.- Returns:
- a random true or false value.
-
nextBytes
Given a byte array as a parameter, this will fill the array with random bytes (modifying it in-place). CallsnextLong()
Math.ceil(bytes.length / 8.0)
times.- Parameters:
bytes
- a byte array that will have its contents overwritten with random bytes.
-
setSeed
Sets the seed of this generator (which is also the current state).- Parameters:
seed
- the seed to use for this PermutedRNG, as if it was constructed with this seed.
-
setState
Sets the seed (also the current state) of this generator.- Specified by:
setState
in interfaceStatefulRandomness
- Parameters:
seed
- the seed to use for this PermutedRNG, as if it was constructed with this seed.
-
getState
Gets the current state of this generator.- Specified by:
getState
in interfaceStatefulRandomness
- Returns:
- the current seed of this PermutedRNG, changed once per call to
nextLong()
-
skip
Advances or rolls back the PermutedRNG'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 tonextLong()
, and returns the random number produced at that step (you can get the state withgetState()
). Skipping ahead or behind takes more than constant time, unlike withLightRNG
, but less time than callingnextLong()
advance
times. Skipping backwards by one step is the worst case for this technique.- Specified by:
skip
in interfaceSkippingRandomness
- Parameters:
advance
- Number of future generations to skip past. Can be negative to backtrack.- Returns:
- the number that would be generated after generating advance random numbers.
-
toString
-
determine
Given suitably-different inputs asstate
, this will permute that state to get a seemingly-unrelated number. UnlikeLightRNG.determine(long)
, this will not work with inputs that are sequential, and it is recommended that subsequent calls change state with a linear congruential generator likePermutedRNG.determine(state = state * 0x5851F42D4C957F2DL + 0x14057B7EF767814FL)
. It will be correct for any inputs, but ifstate
is 0, then this will return 0.- Parameters:
state
- a long that should be changed withstate = state * 0x5851F42D4C957F2DL + 0x14057B7EF767814FL
- Returns:
- a pseudo-random long determined from state
-
determineBounded
Given suitably-different inputs asstate
, this will permute that state to get a seemingly-unrelated number as an int between 0 and bound. UnlikeLightRNG.determine(long)
, this will not work with inputs that are sequential, and it is recommended that subsequent calls change state with a linear congruential generator likePermutedRNG.determine(state = state * 0x5851F42D4C957F2DL + 0x14057B7EF767814FL)
. It will be correct for any inputs, but ifstate
is 0, then this will return 0.- Parameters:
state
- a long that should be changed withstate = state * 0x5851F42D4C957F2DL + 0x14057B7EF767814FL
bound
- the exclusive outer bound on the numbers this can produce, as an int- Returns:
- a pseudo-random int between 0 (inclusive) and bound (exclusive) determined from state
-