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

squidpony.squidmath.MiniMover64RNG 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;

/**
 * The fastest generator in this library on desktop JVMs; one of Mark Overton's subcycle generators from
 * this article, specifically a CMR with a 64-bit state, that has
 * its result multiplied by a constant. Its period is unknown, and it has multiple subcycles with different period
 * lengths, but one is at the very least 2 to the 42, since the generator passes PractRand after generating that many
 * 64-bit integers (it passes 32TB with no anomalies), and all states that can be produced using {@link #seed(int)} have
 * a minimum period of 2 to the 20, or roughly one million. It also can pass TestU01's BigCrush suite in both forward
 * and reversed bit order, though not for all seeds (as expected).
 * 
* Notably, this generator's {@link #nextLong()} method is extremely small (as are all of the methods that use it as a * basis), which may help with inlining decisions for HotSpot. Generating the next step just needs a bitwise rotation of * the current state, multiplying the result by a 32-bit constant, and assigning that to state. Generating a long after * that only needs a multiplication by another constant, which doesn't have the issues with reversed-bits testing that * some other generators do when they multiply as their only output step (xorshift128 notably has patterns in its low * bits, so multiplying by a constant doesn't help those bits, but the CMR generator here has no such low-bit problems). * This means only three math instructions need to be performed to get a new random number (bitwise rotate left, * multiply, multiply), which is extremely low for a high-quality generator. While the CMR state change does not * pipeline well, the ease of calculating a complete number seems to make up for it on desktop JVMs. *
* The choice of constants for the multipliers and for the rotation needs to be carefully verified; earlier choices came * close to failing PractRand at 8TB (and were worsening, so were likely to fail at 16TB), but this set of constants has * higher quality in testing. Other constants did well in PractRand but had failures in TestU01 (even down to SmallCrush * when reversed). For transparency, the constants used in this version: *
    *
  • The state multiplier is 0xAC564B05L (or 2891336453L), which is one of the constants evaluated in Tables of Linear * Congruential Generators of Different Sizes and Good Lattice Structure, by Pierre L'Ecuyer, to be optimal for an LCG * with a modulus of 2 to the 32 and an odd addend (this doesn't have an addend, but it isn't an LCG either).
  • *
  • The post-processing multiplier is 0x818102004182A025L (or -9115001970698837979L), which is a probable prime with * a low Hamming weight (14 bits of 64 are set), in the hope it will perform well speed-wise. This number doesn't seem * as critical to the quality of the generator, and some other multipliers probably work just as well.
  • *
  • A left rotation constant of 29, which was chosen because it seems to allow the generator to pass certain * TestU01 statistical tests, such as Birthday Spacings, where most other rotations do not.
  • *
*
* This is a RandomnessSource but not a StatefulRandomness because it needs to take care and avoid seeds that would put * it in a short-period subcycle. One long test brute-force checked all seeds from 1 to {@code Math.pow(2, 25)} and * validated that each of their periods is at least {@code Math.pow(2, 20) - 1}. This means that as long as a period * as low as 1 million is rarely allowed, a starting state can be quickly chosen from a 32-bit int by using the bottom * 25 bits of the int (plus 1, to disallow the 0 state) and using the remaining 7 bits to step up to 127 times through * the generator. *
* The name comes from M. Overton, who discovered this category of subcycle generators, and also how this generator can * really move when it comes to speed. This generator has less state than {@link Mover64RNG}, has a shorter period than * it, and is faster than it in all aspects. *
* Created by Tommy Ettinger on 11/26/2018. * @author Mark Overton * @author Tommy Ettinger */ public final class MiniMover64RNG implements RandomnessSource, Serializable { private static final long serialVersionUID = 2L; private long state; /** * Calls {@link #seed(int)} with a random int value (obtained using {@link Math#random()}). */ public MiniMover64RNG() { seed((int)((Math.random() * 2.0 - 1.0) * 0x80000000)); } /** * The recommended constructor, this guarantees the generator will have a period of at least 2 to the 20 (roughly * one million, but most if not all initial states will have much longer periods). All ints are permissible values * for {@code state}. * @param state any int; will be used to get the actual state used in the generator (which is a long internally) */ public MiniMover64RNG(final int state) { seed(state); } /** * Not advised for external use; prefer {@link #MiniMover64RNG(int)} because it guarantees a good subcycle. This * constructor allows all subcycles to be produced, including ones with a shorter period. * @param state the state to use, exactly unless it is 0 (then this uses 1) */ public MiniMover64RNG(final long state) { this.state = state == 0L ? 1L : state; } /** * Seeds the state using all bits of the given int {@code s}. Between 33554432 and 4294967296 seeds are possible, * with the actual count probably much closer to 4294967296. This treats the bottom 25 bits of {@code s} (plus 1, to * avoid a seed of 0) as the starting point and then generates the next state at most 127 times, with * each generated state taking less time than {@link #nextLong()}. Some of the starting states are entirely possible * to be within 127 steps of another starting state, so not all seeds are necessarily unique. This is not * guaranteed to put the generator on an optimal subcycle, but it is guaranteed that any subcycle will have a period * of at least 1048575. * @param s all bits are used, none verbatim (0 is tolerated) */ public final void seed(final int s) { long v = (s & 0x1FFFFFF) + 1L; // at least 2 to the 25 sequential seeds have periods of at least 1048575. for (int i = s >>> 25; i > 0; i--) { v = (v << 29 | v >>> 35) * 0xAC564B05L; } state = v; } public final int nextInt() { return (int)((state = (state << 29 | state >>> 35) * 0xAC564B05L) * 0x818102004182A025L); } @Override public final int next(final int bits) { return (int)((state = (state << 29 | state >>> 35) * 0xAC564B05L) * 0x818102004182A025L) >>> (32 - bits); } @Override public final long nextLong() { return (state = (state << 29 | state >>> 35) * 0xAC564B05L) * 0x818102004182A025L; // return (state = (state << 21 | state >>> 43) * 0x9E3779B9L) * 0x41C64E6DL; // earlier, fails some of TestU01 } /** * Gets a pseudo-random double between 0.0 (inclusive) and 1.0 (exclusive). * @return a pseudo-random double between 0.0 (inclusive) and 1.0 (exclusive) */ public final double nextDouble() { return ((state = (state << 29 | state >>> 35) * 0xAC564B05L) * 0x818102004182A025L & 0x1fffffffffffffL) * 0x1p-53; } /** * Gets a pseudo-random float between 0.0f (inclusive) and 1.0f (exclusive). * @return a pseudo-random float between 0.0f (inclusive) and 1.0f (exclusive) */ public final float nextFloat() { return ((state = (state << 29 | state >>> 35) * 0xAC564B05L) * 0x818102004182A025L & 0xffffffL) * 0x1p-24f; } /** * Produces a copy of this MiniMover64RNG 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 MiniMover64RNG */ @Override public MiniMover64RNG copy() { return new MiniMover64RNG(state); } /** * Gets the state; if this generator was set with {@link #MiniMover64RNG()}, * {@link #MiniMover64RNG(int)}, or {@link #seed(int)}, then this will be on a good subcycle, otherwise it * may not be. * @return the state, a long */ public long getState() { return state; } /** * Sets the state to any long, which may put the generator in a low-period subcycle. * Use {@link #seed(int)} to guarantee a good subcycle. * @param state any int */ public void setState(final long state) { this.state = state == 0L ? 1L : state; } @Override public String toString() { return "MiniMover64RNG with state 0x" + StringKit.hex(state); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; MiniMover64RNG miniMover64RNG = (MiniMover64RNG) o; return state == miniMover64RNG.state; } @Override public int hashCode() { return (int)(state ^ state >>> 32); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy