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

emu.squidpony.squidmath.Zag32RNG 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 32-bit generator that needs no math on 64-bit values to calculate high-quality pseudo-random numbers, and has been
 * adapted to meet the needs of GWT while maintaining compatibility with other JDK platforms. It won't ever multiply an
 * int by a number with more than 20 bits, and along with bitwise operations used frequently, this prevents precision
 * loss on GWT from when an int fails to overflow and exceeds 2 to the 53. The only other generator in this library that
 * avoids GWT precision loss like that is {@link ThrustAlt32RNG}, but that generator isn't capable of producing all int
 * results and has a small bias toward other results. All generators that perform their internal operations with long
 * values, such as {@link LightRNG}, aren't affected by the lack of overflow on ints with GWT. {@link LongPeriodRNG} and
 * {@link MersenneTwister} use an int counter with long values for state, but they manually wrap the counter long before
 * overflow would occur. However, all the math on longs has a hefty performance penalty on GWT, even for normally very
 * fast generators like LightRNG, while Zag32RNG is able to safely use ints with much higher performance.
 * 
* Zag32RNG has extremely good speed on GWT, though how good depends on the browser being used and whether 32-bit or * 64-bit results are needed. In Opera 51 on a Windows 7 laptop (64-bit), it takes roughly 43 microseconds to * generate 10,000 int values with Zag32RNG; ThrustAlt32RNG takes roughly 37 microseconds, and LightRNG takes roughly * 1929 microseconds (yes, math on long values does have a speed penalty to make up for its better accuracy). To * generate 10,000 long values with Zag32RNG, it takes roughly 891 microseconds; ThrustAlt32RNG takes 1368 microseconds * (an odd slowdown for a normally-faster generator), and LightRNG takes roughly 2480 microseconds. The total period of * Zag32RNG is 0xFFFFFFFF00000000 (18446744069414584320). Quality is very good here, and this passes PractRand without * failures and with 3 minor anomalies (classified as "unusual" and all different kinds) on 32TB of testing; this is * probably enough to confirm a generator as high-quality. It's still possible that tests not present in PractRand can * detect errors in Zag32RNG. *
* For the specific structure of Zag32RNG, it is a combination of a Marsaglia-style XorShift generator with an "XLCG" * generator (like a linear congruential generator but using XOR instead of addition). It has 2 ints for state, stateA * and stateB, where stateA is modified by the XorShift and cannot be given 0, and stateB is modified by the XLCG and * can be assigned any int. stateA will never be 0, but will be every non-0 int with equal frequency; it repeats every * {@code Math.pow(2, 32) - 1} generated numbers. stateB goes through every int over the course of the XLCG's period; it * naturally has a period of {@code Math.pow(2, 32)}. Just adding an XLCG to a XorShift generator isn't enough to ensure * quality; the XLCG's result (not the actual stateB) is xorshifted right twice, as in {@code tempB ^= tempB >>> 13}, * before adding the XorShift result of stateA and then xorshifting right once more to get the final value. The XorShift * that stateA uses is a normal kind that involves two rightward shifts and one leftward shift, but because stateB is * updated by an XLCG (which has a higher period on its more-significant bits and a very low period on its * least-significant bits), the numbers using stateB's value need only rightward shifts by varying amounts. This * generator is not reversible given the output of {@link #nextInt()}, though the update steps for stateA and stateB are * both individually reversible. *
* Although Zag32RNG has a {@link #determine(int, int)} method, calling it is considerably more complex than other * RandomnessSources that provide determine() methods. It also doesn't allow skipping through the state, and a moderate * amount of the possible values that can be provided with {@link #setState(long)} will be changed before this can use * them (there are fewer than 2 to the 64 possible states, but only somewhat). *
* Created by Tommy Ettinger on 3/13/2018. */ public final class Zag32RNG implements StatefulRandomness, Serializable { private int a, b; private static final long serialVersionUID = 178316585712476930L; /** * Constructs a Zag32RNG with a random state, using two calls to Math.random(). */ public Zag32RNG() { this((int)((Math.random() * 2.0 - 1.0) * 0x80000000), (int)((Math.random() * 2.0 - 1.0) * 0x80000000)); } /** * Constructs a Zag32RNG with a stateA equal to the given stateA unless 0 was given, in which case stateA will be 1, * and a stateB exactly equal to the given stateB. * @param stateA any int except 0 (0 will be treated as 1 instead) * @param stateB any int */ public Zag32RNG(int stateA, int stateB) { a = stateA == 0 ? 1 : stateA; b = stateB; } /** * Takes 32 bits of state and uses it to randomly fill the 64 bits of state this uses. * @param statePart any int */ public Zag32RNG(int statePart) { b = statePart; a = ~statePart; a ^= a >>> 5; a ^= a << 17; a ^= a >>> 13; a ^= statePart; if(a == 0) a = 1; // not sure if this is possible } /** * Constructs a Zag32RNG using a long that combines the two parts of state, as from {@link #getState()}. * @param stateCombined a long that combines state A and state B, with state A in the less significant 32 bits */ public Zag32RNG(long stateCombined) { this((int)stateCombined, (int)(stateCombined >>> 32)); } public final int nextInt() { int z = (b = (b ^ 0xC74EAD55) * 0xA5CB3 | 0); a ^= a >>> 14; z ^= z >>> 13; a ^= a >>> 15; z = (z ^ z >>> 11) + (a ^= a << 13) | 0; return (z ^ z >>> 7); } /** * 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(int bits) { int z = (b = (b ^ 0xC74EAD55) * 0xA5CB3 | 0); a ^= a >>> 14; z ^= z >>> 13; a ^= a >>> 15; z = (z ^ z >>> 11) + (a ^= a << 13) | 0; return (z ^ z >>> 7) >>> (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() { int z = (b ^ 0xC74EAD55) * 0xA5CB3 | 0, y = (b = (z ^ 0xC74EAD55) * 0xA5CB3 | 0); a ^= a >>> 14; z ^= z >>> 13; a ^= a >>> 15; z = (z ^ z >>> 11) + (a ^= a << 13) | 0; a ^= a >>> 14; y ^= y >>> 13; a ^= a >>> 15; y = (y ^ y >>> 11) + (a ^= a << 13) | 0; return (long)(y ^ y >>> 7) << 32 ^ (z ^ z >>> 7); } /** * 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 Zag32RNG copy() { return new Zag32RNG(a, b); } public int getStateA() { return a; } public void setStateA(int stateA) { a = stateA == 0 ? 1 : stateA; } public int getStateB() { return b; } public void setStateB(int stateB) { b = stateB; } /** * Get the current internal state of the StatefulRandomness as a long. * * @return the current internal state of this object. */ @Override public long getState() { return (long) b << 32 | (a & 0xFFFFFFFFL); } /** * Set the current internal state of this StatefulRandomness with a long. * If the bottom 32 bits of the given state are all 0, then this will set stateA to 1, otherwise it sets stateA to * those bottom 32 bits. This always sets stateB to the upper 32 bits of the given state. * @param state a 64-bit long; the bottom 32 bits should not be all 0, but this is tolerated */ @Override public void setState(long state) { a = (int)(state & 0xFFFFFFFFL); if(a == 0) a = 1; b = (int) (state >>> 32); } /** * Sets the current internal state of this Zag32RNG with two ints, where stateA can be any int except 0, and stateB * can be any int. * @param stateA any int except 0 (0 will be treated as 1 instead) * @param stateB any int */ public void setState(int stateA, int stateB) { a = stateA == 0 ? 1 : stateA; b = stateB; } @Override public String toString() { return "Zag32RNG with stateA 0x" + StringKit.hex(a) + " and stateB 0x" + StringKit.hex(b); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Zag32RNG that = (Zag32RNG) o; return a == that.a && b == that.b; } @Override public int hashCode() { return 31 * a + b | 0; } /** * Gets a pseudo-random int determined wholly by the given state variables a and b, which should change every call. * Call with {@code determine((a = (a = (a ^= a >>> 14) ^ a >>> 15) ^ a << 13), b = (b ^ 0xC74EAD55) * 0xA5CB3 | 0)}, * where a and b are int variables used as state. The complex call to this method allows it to remain static. In the * update for b, the bitwise OR with 0 is only needed for GWT in order to force overflow to wrap instead of losing * precision (a quirk of the JS numbers GWT uses). If you know you won't target GWT you can use * {@code b = (b ^ 0xC74EAD55) * 0xA5CB3}. This should be fairly fast on GWT because most PRNGs (that can pass any * decent statistical quality tests) use either 64-bit long values or many int variables for state, while this can * get by with just two int variables. Using long on GWT is usually the only reasonable option if you expect * arithmetic overflow (because long is emulated by GWT to imitate the behavior of a JDK, but is considerably slower * as a result), but by some careful development for this generator, it should have identical results on GWT and on * desktop/Android JVMs with only int math. *
* You may find it more convenient to just instantiate a Zag32RNG object rather than using the complex state update; * using the methods on a Zag32RNG object may also be more efficient because some operations can be performed with * bit-parallel optimizations on the same (modern) processor. * @param a must be non-zero and updated with each call, using {@code a = (a = (a ^= a >>> 14) ^ a >>> 15) ^ a << 13} * @param b must be updated with each call, using {@code b = (b ^ 0xC74EAD55) * 0xA5CB3 | 0}, with {@code | 0} needed for GWT * @return a pseudo-random int, equidistributed over a period of 0xFFFFFFFF00000000; can be any int */ public static int determine(int a, int b) { return ((b = ((b ^= b >>> 13) ^ b >>> 11) + a | 0) ^ b >>> 7); } /** * Gets a pseudo-random int between 0 and the given bound, using the given ints a and b as the state; these state * variables a and b should change with each call. The exclusive bound can be negative or positive, and can be * between -32768 and 32767 (both inclusive); the small limits allow using just 32-bit math. Call with * {@code determineBounded((a = (a = (a ^= a >>> 14) ^ a >>> 15) ^ a << 13), b = (b ^ 0xC74EAD55) * 0xA5CB3 | 0), bound}, * where a and b are int variables used as state and bound is the exclusive outer bound. The complex call to this * method allows it to remain static. In the update for b, the bitwise OR with 0 is only needed for GWT in order to * force overflow to wrap instead of losing precision (a quirk of the JS numbers GWT uses). If you know you won't * target GWT you can use {@code b = (b ^ 0xC74EAD55) * 0xA5CB3}. * @param a must be non-zero and updated with each call, using {@code a = (a = (a ^= a >>> 14) ^ a >>> 15) ^ a << 13} * @param b must be updated with each call, using {@code b = (b ^ 0xC74EAD55) * 0xA5CB3 | 0}, with {@code | 0} needed for GWT * @param bound the outer exclusive limit on the random number; should be between -32768 and 32767 (both inclusive) * @return a pseudo-random int, between 0 (inclusive) and bound (exclusive) */ public static int determineBounded(int a, int b, int bound) { return ((bound * (((b = ((b ^= b >>> 13) ^ b >>> 11) + a | 0) ^ b >>> 7) & 0x7FFF)) >> 15); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy