All Downloads are FREE. Search and download functionalities are using the official Maven repository.

squidpony.squidmath.ThrustAltRNG Maven / Gradle / Ivy

Go to download

SquidLib platform-independent logic and utility code. Please refer to https://github.com/SquidPony/SquidLib .

There is a newer version: 3.0.6
Show newest version
package squidpony.squidmath;

import squidpony.StringKit;

import java.io.Serializable;


/**
 * A random number generator that is extremely fast but can't return all possible results. ThrustAltRNG passes TestU01's
 * BigCrush, which is a difficult statistical quality test. On gjrand's
 * "testunif" checks, this does very well on 100GB of tested data, with the "Overall summary one sided P-value P =
 * 0.981", where 1 is perfect and 0.1 or less is a failure. On PractRand,
 * this passes all 32TB of generated numbers without finding any failures (and very rarely finding anomalies). Like
 * {@link LightRNG}, this changes its state with a steady fixed increment, and does hash-like adjustments to the
 * current state to randomize it (the change is not a cipher because it is not reversible; this may be an advantage
 * for some usage). The period on ThrustAltRNG is 2 to the 64. ThrustAltRNG is very strong on speed, outpacing the
 * default generator for {@link RNG}, {@link DiverRNG}, by a small margin, and most other RandomnessSources in
 * SquidLib by a larger margin (it is slower than {@link MiniMover64RNG}, but this has a better period). Similarly to
 * other hash-like PRNGs such as {@link LightRNG}, ThrustAltRNG has a {@link #determine(long)} method that takes a state
 * as a long and returns a deterministic random number (each input has one output, though in this case the reverse isn't
 * true and some outputs will be returned by multiple inputs). Like LightRNG, but unlike an LCG such as
 * {@link java.util.Random}, changing the seed even slightly generally produces completely different results, which
 * applies primarily to determine() but also the first number generated in a series of nextLong() calls. This generator
 * is GWT-safe but will be much slower on GWT than generators designed for usage there, such as {@link GWTRNG} or
 * {@link Lathe32RNG}.
 * 
* Because this generator can't produce all longs (it is not equidistributed), that alone is enough to discount its use * in some (mainly scientific) scenarios, although it passes all major testing suites (TestU01's BigCrush, PractRand * over the full 32TB of tests, and gjrand to some degree, at least better than most). DiverRNG is the default * generator after ThrustAltRNG was used extensively for some time, since DiverRNG passes the same tests, is almost as * fast, and is known to produce all longs over the course of its period. One small change was required to pass a test * added in PractRand version 0.94, going from a shift of 22 to a shift of 23 at the end of the generation. Without this * change, ThrustAltRNG will eventually fail PractRand (failing "TMFn" tests, which check for patterns like those in a * linear congruential generator such as {@link java.util.Random}), but only after 16TB of data has been analyzed. Even * if using a version before the shift was changed on June 8, 2019, the quality is probably fine. *
* There was a ThrustRNG in SquidLib, but it failed statistical tests badly in roughly a minute of testing, so even * though it was faster it probably wasn't a good idea to use it. ThrustAltRNG modifies ThrustRNG's algorithm very * heavily, and isn't especially similar, but the name stuck, I guess. The idea behind the name is that the generator is * acting like a thrust in fencing, pushing quickly but leaving a hole (not in the quality, but in the distribution). *
* Created by Tommy Ettinger on 10/18/2017. */ public final class ThrustAltRNG implements StatefulRandomness, SkippingRandomness, Serializable { private static final long serialVersionUID = 3L; /** * Can be any long value. */ public long state; /** * Creates a new generator seeded using Math.random. */ public ThrustAltRNG() { this((long) ((Math.random() - 0.5) * 0x10000000000000L) ^ (long) (((Math.random() - 0.5) * 2.0) * 0x8000000000000000L)); } public ThrustAltRNG(final long seed) { state = seed; } /** * Get the current internal state of the StatefulRandomness as a long. * * @return the current internal state of this object. */ @Override public long getState() { return state; } /** * Set the current internal state of this StatefulRandomness with a long. * * @param state a 64-bit long */ @Override public void setState(long state) { this.state = state; } /** * Using this method, any algorithm that might use the built-in Java Random * can interface with this randomness source. * * @param bits the number of bits to be returned * @return the integer containing the appropriate number of bits */ @Override public final int next(final int bits) { final long s = (state += 0x6C8E9CF570932BD5L); final long z = (s ^ (s >>> 25)) * (s | 0xA529L); return (int)(z ^ (z >>> 23)) >>> (32 - bits); } /** * 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). * * @return a random long between Long.MIN_VALUE and Long.MAX_VALUE (both inclusive) */ @Override public final long nextLong() { final long s = (state += 0x6C8E9CF570932BD5L); final long z = (s ^ (s >>> 25)) * (s | 0xA529L); return z ^ (z >>> 23); } /** * Advances or rolls back the ThrustAltRNG'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 to nextLong(), * and returns the random number produced at that step (you can get the state with {@link #getState()}). * * @param advance Number of future generations to skip over; can be negative to backtrack, 0 gets the most-recently-generated number * @return the random long generated after skipping forward or backwards by {@code advance} numbers */ @Override public final long skip(long advance) { final long s = (state += 0x6C8E9CF570932BD5L * advance); final long z = (s ^ (s >>> 25)) * (s | 0xA529L); return z ^ (z >>> 23); } /** * 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. * * @return a copy of this RandomnessSource */ @Override public ThrustAltRNG copy() { return new ThrustAltRNG(state); } @Override public String toString() { return "ThrustAltRNG with state 0x" + StringKit.hex(state) + 'L'; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ThrustAltRNG thrustAltRNG = (ThrustAltRNG) o; return state == thrustAltRNG.state; } @Override public int hashCode() { return (int) (state ^ (state >>> 32)); } /** * Returns a random permutation of state; if state is the same on two calls to this, this will return the same * number. This is expected to be called with some changing variable, e.g. {@code determine(++state)}, where * the increment for state should be odd but otherwise doesn't really matter. This multiplies state by * {@code 0x6C8E9CF570932BD5L} 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. * @param state a variable that should be different every time you want a different random result; * using {@code determine(++state)} is recommended to go forwards or {@code determine(--state)} to * generate numbers in reverse order * @return a pseudo-random permutation of state */ public static long determine(long state) { return (state = ((state *= 0x6C8E9CF570932BD5L) ^ (state >>> 25)) * (state | 0xA529L)) ^ (state >>> 23); } //for quick one-line pastes of how the algo can be used with "randomize(++state)" //public static long randomize(long state) { return (state = ((state *= 0x6C8E9CF570932BD5L) ^ (state >>> 25)) * (state | 0xA529L)) ^ (state >>> 23); } /** * 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. This is expected to be called with a changing variable, e.g. {@code determine(++state)}, * where the increment for state should be odd but otherwise doesn't really matter. This multiplies state by * {@code 0x6C8E9CF570932BD5L} 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 * only 2 to the 30 possible floats between 0 and 1. * @param state a variable that should be different every time you want a different random result; * using {@code determine(++state)} is recommended to go forwards or {@code determine(--state)} to * generate numbers in reverse order * @return a pseudo-random float between 0f (inclusive) and 1f (exclusive), determined by {@code state} */ public static float determineFloat(long state) { return (((state = ((state *= 0x6C8E9CF570932BD5L) ^ (state >>> 25)) * (state | 0xA529L)) ^ (state >>> 23)) & 0xFFFFFF) * 0x1p-24f; } /** * 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. This is expected to be called with a changing variable, e.g. * {@code determine(++state)}, where the increment for state should be odd but otherwise doesn't really matter. This * multiplies state by {@code 0x6C8E9CF570932BD5L} 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 only 2 to the 62 possible doubles between 0 and 1. * @param state a variable that should be different every time you want a different random result; * using {@code determine(++state)} is recommended to go forwards or {@code determine(--state)} to * generate numbers in reverse order * @return a pseudo-random double between 0.0 (inclusive) and 1.0 (exclusive), determined by {@code state} */ public static double determineDouble(long state) { return (((state = ((state *= 0x6C8E9CF570932BD5L) ^ (state >>> 25)) * (state | 0xA529L)) ^ (state >>> 23)) & 0x1FFFFFFFFFFFFFL) * 0x1p-53; } /** * Given a state that should usually change each time this is called, and a bound that limits the result to some * (typically fairly small) int, produces a pseudo-random int between 0 and bound (exclusive). The bound can be * negative, which will cause this to produce 0 or a negative int; otherwise this produces 0 or a positive int. * The state should change each time this is called, generally by incrementing by an odd number (not an even number, * especially not 0). It's fine to use {@code determineBounded(++state, bound)} to get a different int each time. * The period is usually 2 to the 64 when you increment or decrement by 1, but some bounds may reduce the period (in * the extreme case, a bound of 1 would force only 0 to be generated, so that would make the period 1). * @param state a variable that should be different every time you want a different random result; * using {@code determineBounded(++state, bound)} is recommended to go forwards or * {@code determineBounded(--state, bound)} to generate numbers in reverse order * @param bound the outer exclusive bound for the int this produces; can be negative or positive * @return a pseudo-random int between 0 (inclusive) and bound (exclusive) */ public static int determineBounded(long state, final int bound) { return (int)((bound * ( ((state = ((state *= 0x6C8E9CF570932BD5L) ^ (state >>> 25)) * (state | 0xA529L)) ^ (state >>> 23)) & 0xFFFFFFFFL)) >> 32); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy