public class LinnormRNG extends java.lang.Object implements RandomnessSource, StatefulRandomness, java.io.Serializable
DiverRNG
is a variant that is a little faster,
while keeping the same other qualities, so it is currently recommended over LinnormRNG (however, the same structure
is shared between LinnormRNG and MizuchiRNG
, and Mizuchi has some extra features that Diver lacks). Has 64
bits of state and natively outputs 64 bits at a time, changing the state with a basic linear congruential generator
(it is simply state = state * 3935559000370003845L + 1L
, with the large multiplier found by L'Ecuyer in a
1999 paper). Starting with that LCG's output, it xorshifts that output twice (using
z ^= (z >>> 23) ^ (z >>> 47);
), multiplies by a very large negative long, then returns another xorshift.
Considering that some seeds don't have any anomalies in 8TB with Linnorm, the quality is probably fine except for the
known potential issue that it can't return duplicate outputs until its period has cycled.
ThrustAltRNG
and MiniMover64RNG
are faster (tied for first place), but unlike those, Linnorm can
produce all long values as output; ThrustAltRNG bunches some outputs and makes producing them more likely while
others can't be produced at all, while MiniMover64RNG cycles at some point before 2 to the 64 but after 2 to the 42
(it doesn't produce any duplicates until then, but it also can't produce all values). Notably, this generator is
faster than LightRNG
with similar quality, and also faster than XoRoRNG
while passing tests that
XoRoRNG always or frequently fails (and fails early), such as binary matrix rank tests. It is slower than
DiverRNG
, which is a variant on the structure of LinnormRNG.
nextLong()
. MizuchiRNG
uses the same algorithm except for the number added
in the LCG state update; here this number is always 1, but in MizuchiRNG it can be any odd long. This means that any
given MizuchiRNG object has two long values stored in it instead of the one here, but it allows two MizuchiRNG
objects with different streams to produce different, probably-not-correlated sequences of results, even with the same
seed. This property may be useful for cases where an adversary is trying to predict results in some way, though using
different streams for this purpose isn't enough and should be coupled with truncation of a large part of output (see
PCG-Random's techniques for this).
Constructor and Description |
---|
LinnormRNG()
Constructor that seeds the generator with two calls to Math.random.
|
LinnormRNG(java.lang.CharSequence seed)
Constructor that hashes seed with
CrossHash.hash64(CharSequence) and uses the result as the state. |
LinnormRNG(long seed)
Constructor that uses the given seed as the state without changes; all long seeds are acceptable.
|
Modifier and Type | Method and Description |
---|---|
LinnormRNG |
copy()
Produces a copy of this LinnormRNG 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() |
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.
|
java.lang.String |
toString() |
public LinnormRNG()
public LinnormRNG(long seed)
seed
- any long, will be used exactlypublic LinnormRNG(java.lang.CharSequence seed)
CrossHash.hash64(CharSequence)
and uses the result as the state.seed
- any CharSequence, such as a String or StringBuilder; should probably not be null (it might work?)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 LinnormRNG copy()
copy
in interface RandomnessSource
copy
in interface StatefulRandomness
public int nextInt()
public int nextInt(int 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, should be greater than innerpublic long nextLong(long bound)
nextLong()
, where rejection sampling would sometimes advance by one call, but other times by
arbitrarily many more.
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 LinnormRNG, as if it was constructed with this seed.public long getState()
getState
in interface StatefulRandomness
public 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)
LinnormRNG.determine(++state)
or LinnormRNG.determine(--state)
to
produce a sequence of different numbers, but you can also use LinnormRNG.determine(state += 12345L)
or
any odd-number increment. All longs are accepted by this method, and all longs can be produced; unlike several
other classes' determine() methods, passing 0 here does not 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)
LinnormRNG.determineBounded(++state, bound)
or LinnormRNG.determineBounded(--state, bound)
to
produce a sequence of different numbers, but you can also use
LinnormRNG.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 multiplies state by
0x632BE59BD9B4E019L
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
less than 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
multiplies state by 0x632BE59BD9B4E019L
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 less than 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.