package squidpony.squidmath; import squidpony.StringKit; import squidpony.annotation.Beta; import java.io.Serializable; /** * Like PintRNG (only uses 32-bit int math, good for GWT), but much faster at the expense of quality. * This generator is faster than ThunderRNG at generating ints, while also implementing StatefulRandomness. It is slower * but not especially slow at generating longs, and takes between 5% and 10% more time than LightRNG to generate longs * (it takes about 40% less time than LightRNG to generate ints). Quality is unclear, since this relies on some very * particular values for constants and shows various flaws visually when the constants are even slightly off. There are * probably better choices for constants out there that we may be able to find, but it doesn't seem easy. * It's likely that the period of FlapRNG is a full 2 to the 64 (0 seed is allowed), since this uses a pair of ints for * its state, though it may be less (2 to the 32 is absolutely the minimum possible period). * <br> * Created by Tommy Ettinger on 5/1/2017. */ @Beta public class FlapRNG implements StatefulRandomness, Serializable { private static final long serialVersionUID = 1L; public FlapRNG(){ this((int)((Math.random() * 2.0 - 1.0) * 0x80000000)); } public FlapRNG(final int seed) { state0 = seed; state1 = seed ^ seed >>> (4 + (seed >>> 28)); state1 *= 277803737; state1 ^= (state1 >>> 22); } public FlapRNG(final int seed0, final int seed1) { state0 = seed0; state1 = seed1; } public FlapRNG(final long seed) { state0 = (int)(seed & 0xFFFFFFFFL); state1 = (int)(seed >>> 32); } public FlapRNG(final CharSequence seed) { this(CrossHash.Wisp.hash64(seed)); } public int state0, state1; /** * 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)(state1) << 32 | (state0 & 0xFFFFFFFFL); } /** * Set the current internal state of this StatefulRandomness with a long. * * @param state a 64-bit long, but this is always truncated when used; the upper 32 bits are discarded */ @Override public void setState(final long state) { state0 = (int)(state & 0xFFFFFFFFL); state1 = (int)(state >>> 32); } /** * 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 ) { // possible alternative for other cases //return (state0 += (((++state1 * 0xC6BC278D) >>> 28) + 60) * 0x632D978F) >>> (32 - bits); // very good! return (state0 += (((state1 += 0xC6BC278D) >>> 28) + 60) * 0x632D978F) >>> (32 - bits); // less-working //return (state0 += ~(((state1 += 0xC6BC278D) >>> 28) * 0x632D978F)) >>> (32 - bits); //return (state0 += ((state1 += 0xC6BC278D) >> 28) * 0x632DB5AB) * 0x9E3779B9 >>> (32 - bits); } /** * Gets a pseudo-random int, which can be positive or negative but is likely to be drawn from less possible options * than the full range of {@link Integer#MIN_VALUE} to {@link Integer#MAX_VALUE}. Very fast, though. * @return a pseudo-random 32-bit int */ public final int nextInt() { return (state0 += (((state1 += 0xC6BC278D) >>> 28) + 60) * 0x632D978F); //return (state0 += (state1 += 0x9E3779B9) ^ 0x632BE5AB); //return (state0 += (0x632BE5AB ^ (state1 += 0x9E3779B9)) >> 1) * 0xC6BC278D; /* final int s0 = state0; int s1 = state1; final int result = s0 + s1; s1 ^= s0 + 0x9E3779B9; state0 = (s0 << 26 | s0 >>> 6) ^ s1 ^ (s1 << 7); // a, b state1 = (s1 << 18 | s1 >>> 14); // c return result; */ //0x632BE5AB //final int z = (state += 0x9E3779B9); //return (z >> (z & 15)) * 0xC6BC278D; //final int z = (state + 0x9E3779B9); //return state += ((state + 0x9E3779B9) >> 5) * 0xC6BC278D; //final int z = (state += 0x9E3779B9), r = (z & 15); //return (z >> r) * 0xC6BC278D; //return state += (state >> 5) * 0xC6BC279692B5CC83L + 0x9E3779B97F4A7C15L; } /** * Using this method, any algorithm that needs to efficiently generate more * than 32 bits of random data can interface with this randomness source. * This implementation produces a different result than calling {@link #nextInt()} twice and shifting the bits * to make a long from the two ints. It uses mostly the same steps as nextInt(), but instead of multiplying a * 32-bit int by a large constant, it generates a similar 32-bit int (but multiplies by a larger 64-bit constant to * get a long) and XORs two modifications of it (multiplying by a very large long, and left-shifting by 32). The end * result is a long that should take only slightly longer to produce than an int, from a primarily-int generator! * Hooray. The downside is that only 2 to the 32 longs can be produced by this method (not the full 2 to the 64 * range that would be ideal), though the period should be significantly higher than that. * <p> * Pseudo-random results may be between 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 r = (state0 += (((state1 += 0xC6BC278D) >>> 28) + 60) * 0x632D978F); return r * 0xC6BC279692B5CC53L ^ r << 32; // return ((state += 0x9E3779B97F4A7C15L ^ state << 1) >> 16) * 0xC6BC279692B5CC83L; /* final int s0 = state0; final int s1 = state1; return (s0 + s1) ^ (((state0 = s0 + 0x632BE5AB ^ (state1 = s1 + 0x9E3779B9)) >> 13) * 0xC6BC279692B5CC83L) << 32; *//* final int s0 = state0; int s1 = state1; final long result = s0 * 0xD0E89D2D311E289FL + s1 * 0xC6BC279692B5CC83L; s1 ^= s0 + 0x9E3779B9; state0 = (s0 << 26 | s0 >>> 6) ^ s1 ^ (s1 << 7); // a, b state1 = (s1 << 18 | s1 >>> 14); // c return result; */ /* int z = state + 0x9E3779B9; state += (z >> (z >>> 28)) * 0xC6BC279692B5CC83L; z = (state + 0x9E3779B9); return (state) ^ (long)(state += ((z >> (z >>> 28)) * 0xC6BC279692B5CC83L)) << 32; */ } /** * 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 RandomnessSource copy() { return new FlapRNG(state0, state1); } /** * A simple "output stage" applied to state; this method does not update state on its own. If you expect to call * this method more than once, you should perform some extra changes to state as part of the call. The way that * seems to work rather well is to add a large constant, XORed with state left-shifted by 1, to state, and assign * when you make the call. This is clearer with code: {@code FlapRNG.determine(state += 0x9E3779B9 ^ (state << 1))}. * Here, the "large constant" should have at least two of the upper 5 bits set to be safe, and must be odd. * The golden-ratio-derived constant 0x9E3779B9 should be fine, as would 0xF0000001. * This method doesn't offer particularly good quality assurances, but should be very fast. * @param state should be changed when you call this (see above), e.g. {@code state += 0x9E3779B9 ^ (state << 1)} * @return an altered version of state that should be very fast to compute but doesn't promise great quality */ public static int determine(final int state) { return (state >> 13) * 0xC6BC278D; } /** * Gets a pseudo-random float between 0f (inclusive) and 1f (exclusive) using the given state. If you expect to call * this method more than once, you should perform some extra changes to state as part of the call. The way that * seems to work rather well is to add a large constant, XORed with state left-shifted by 1, to state, and assign * when you make the call. In code: {@code FlapRNG.randomFloat(state += 0x9E3779B9 ^ (state << 1))}. * Here, the "large constant" should have at least two of the upper 5 bits set to be safe, and must be odd. * The golden-ratio-derived constant 0x9E3779B9 should be fine, as would 0xF0000001. * @param state any int * @return a pseudo-random float from -0f (inclusive) to 1f (exclusive) */ public static float randomFloat(int state) { return NumberTools.intBitsToFloat((((state >> 13) * 0xC6BC278D) >>> 9) | 0x3f800000) - 1f; } /** * Gets a pseudo-random float between -1f (inclusive) and 1f (exclusive) using the given state. If you expect to * call this method more than once, you should perform some extra changes to state as part of the call. The way that * seems to work rather well is to add a large constant, XORed with state left-shifted by 1, to state, and assign * when you make the call. In code: {@code FlapRNG.randomFloat(state += 0x9E3779B9 ^ (state << 1))}. * Here, the "large constant" should have at least two of the upper 5 bits set to be safe, and must be odd. * The golden-ratio-derived constant 0x9E3779B9 should be fine, as would 0xF0000001. * @param state any int * @return a pseudo-random float from -1f (inclusive) to 1f (exclusive) */ public static float randomSignedFloat(int state) { return NumberTools.intBitsToFloat((((state >> 13) * 0xC6BC278D) >>> 9) | 0x40000000) - 3f; } @Override public String toString() { return "FlapRNG with state0 0x" + StringKit.hex(state0) + ", state1 0x" + StringKit.hex(state1); } @Override public int hashCode() { return 0x632BE5AB * state0 ^ state1; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; FlapRNG flapRNG = (FlapRNG) o; if (state0 != flapRNG.state0) return false; return state1 == flapRNG.state1; } }