public class PulleyRNG extends java.lang.Object implements StatefulRandomness, SkippingRandomness, java.io.Serializable
determine(long)
on two different sequences of inputs (one that added 3 each time, and one that added
5 each time) showed no failures on 32TB of data produced by XORing calls to determine() on both sequences. PulleyRNG
is one-dimensionally equidistributed across all 64-bit outputs, has 64 bits of state, natively outputs 64 bits at a
time, can have its state set and skipped over as a StatefulRandomness
and SkippingRandomness
. It has
a 1-to-1 correspondence between inputs and outputs for determine(long)
, and you can get the input to
determine() that produced a particular long output by passing that output to inverseNextLong(long)
. It is
largely the work of Pelle Evensen, who discovered that where a unary hash (called a determine() method here) can
start with the XOR of the input and two rotations of that input, and that sometimes acts as a better randomization
procedure than multiplying by a large constant (which is what LightRNG.determine(long)
,
LinnormRNG.determine(long)
, and even ThrustAltRNG.determine(long)
do). Evensen also crunched the
numbers to figure out that n ^ n >>> A ^ n >>> B
is a bijection for all distinct non-zero values for A and B,
though this wasn't used in his unary hash rrxmrrxmsx_0.
determine(long)
looks like this (nextLong()
just calls determine() on a counter):
n ^ (n << 41 | n >>> 23) ^ (n << 17 | n >>> 47)
0x369DEA0F31A53F85L
, and store it in nn ^ n >>> 25 ^ n >>> 37
0xDB4F0B9175AE2165L
, and store it in nDiverRNG.randomize(long)
), which is
ridiculously strong (passing the full battery of tests that rrxmrrxmsx_0 only narrowly failed) but not especially
fast. PulleyRNG is an effort to speed up PelicanRNG just a little, but without doing the extensive testing that
ensure virtually any bit pattern given to PelicanRNG will produce pseudo-random outputs. PulleyRNG does well in tough
tests. Other than the input stream correlation test mentioned earlier, this also passes tests if the inputs are
incremented by what is normally one of the worst-case scenarios for other generators -- using an increment that is
the multiplicative inverse (mod 2 to the 64 in this case) of one of the fixed constants in the generator. The first
multiplication performed here is by 0x369DEA0F31A53F85L
, and
0xBE21F44C6018E14DL * 0x369DEA0F31A53F85L == 1L
, so testing determine() with inputs that change by
0xBE21F44C6018E14DL should stress the generator, but instead it does fine through 32TB, with only one "unusual"
anomaly rather early on.
Constructor and Description |
---|
PulleyRNG()
Creates a new generator seeded using Math.random.
|
PulleyRNG(long seed) |
PulleyRNG(java.lang.String seed) |
Modifier and Type | Method and Description |
---|---|
PulleyRNG |
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)
Static randomizing method that takes its state as a parameter; state is expected to change between calls to this.
|
static int |
determineBounded(long state,
int bound)
Static randomizing method that takes its state as a parameter and limits output to an int between 0 (inclusive)
and bound (exclusive); state is expected to change between calls to this.
|
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.
|
boolean |
equals(java.lang.Object o) |
long |
getState()
Gets the current state of this generator.
|
int |
hashCode() |
static long |
inverseNextLong(long out)
Given the output of a call to
nextLong() as out , this finds the state of the PulleyRNG that
produce that output. |
int |
next(int bits)
Using this method, any algorithm that might use the built-in Java Random
can interface with this randomness source.
|
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)
|
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.
|
int |
nextInt(int inner,
int outer)
Inclusive inner, exclusive outer.
|
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 bound (which may be positive or negative), with an inner bound of 0.
|
long |
nextLong(long lower,
long upper)
Inclusive inner, exclusive outer; lower and upper can be positive or negative and there's no requirement for one
to be greater than or less than the other.
|
void |
setState(long seed)
Sets the seed (also the current state) of this generator.
|
long |
skip(long advance)
Advances or rolls back the SkippingRandomness' state without actually generating each number.
|
java.lang.String |
toString() |
public PulleyRNG()
public PulleyRNG(long seed)
public PulleyRNG(java.lang.String seed)
public int next(int bits)
RandomnessSource
next
in interface RandomnessSource
bits
- the number of bits to be returnedpublic long nextLong()
nextLong
in interface RandomnessSource
public PulleyRNG copy()
copy
in interface RandomnessSource
copy
in interface StatefulRandomness
public long skip(long advance)
SkippingRandomness
RandomnessSource.nextLong()
,
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 from RandomnessSource.nextLong()
.skip
in interface SkippingRandomness
advance
- Number of future generations to skip over; can be negative to backtrack, 0 gets the most-recently-generated numberadvance
numberspublic int nextInt()
public int nextInt(int bound)
IRNG.nextSignedInt(int)
if you need a negative outer bound.bound
- the upper bound; should be positivepublic int nextInt(int inner, int outer)
inner
- the inner bound, inclusive, can be positive or negativeouter
- the outer bound, exclusive, can be positive or negative, usually greater than innerpublic long nextLong(long bound)
nextLong()
.bound
- the outer exclusive bound; can be positive or negativepublic long nextLong(long lower, long upper)
lower
- the lower bound, inclusive, can be positive or negativeupper
- the upper bound, exclusive, can be positive or negativepublic double nextDouble()
public double nextDouble(double outer)
outer
- the exclusive outer bound, can be negativepublic float nextFloat()
public boolean nextBoolean()
public void nextBytes(byte[] bytes)
Math.ceil(bytes.length / 8.0)
times.bytes
- a byte array that will have its contents overwritten with random bytes.public void setState(long seed)
setState
in interface StatefulRandomness
seed
- the seed to use for this PulleyRNG, as if it was constructed with this seed.public long getState()
getState
in interface StatefulRandomness
public static long inverseNextLong(long out)
nextLong()
as out
, this finds the state of the PulleyRNG that
produce that output. If you set the state of a PulleyRNG with setState(long)
to the result of this
method and then call nextLong()
on it, you should get back out
. This can also reverse
determine(long)
; it uses the same algorithm as nextLong().
nextLong()
, but both run in constant time.
nextLong(long)
method by any generator.out
- a long as produced by nextLong()
, without changespublic java.lang.String toString()
toString
in class java.lang.Object
public boolean equals(java.lang.Object o)
equals
in class java.lang.Object
public int hashCode()
hashCode
in class java.lang.Object
public static long determine(long state)
PulleyRNG.determine(++state)
or PulleyRNG.determine(--state)
to
produce a sequence of different numbers, but you can also use PulleyRNG.determine(state += 12345L)
or
any odd-number increment. All longs are accepted by this method, and all longs can be produced; passing 0 here
will return 0.state
- any long; subsequent calls should change by an odd number, such as with ++state
public static int determineBounded(long state, int bound)
PulleyRNG.determineBounded(++state, bound)
or PulleyRNG.determineBounded(--state, bound)
to
produce a sequence of different numbers, but you can also use
PulleyRNG.determineBounded(state += 12345L, bound)
or any odd-number increment. All longs are accepted
by this method, but not all ints between 0 and bound are guaranteed to be produced with equal likelihood (for any
odd-number values for bound, this isn't possible for most generators). The bound can be negative.state
- any long; subsequent calls should change by an odd number, such as with ++state
bound
- the outer exclusive bound, as an intpublic static float determineFloat(long state)
determine(++state)
,
where the increment for state should be odd but otherwise doesn't really matter. This should tolerate just about
any increment 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.state
- a variable that should be different every time you want a different random result;
using determine(++state)
is recommended to go forwards or determine(--state)
to
generate numbers in reverse orderstate
public static double determineDouble(long state)
determine(++state)
, where the increment for state should be odd but otherwise doesn't really matter. This
should tolerate just about any increment, 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.state
- a variable that should be different every time you want a different random result;
using determine(++state)
is recommended to go forwards or determine(--state)
to
generate numbers in reverse orderstate
Copyright © Eben Howard 2012–2022. All rights reserved.