/* * @(#) PseudoRandom.java - Class for pseudo-random generator. * (c) 2000 Ivan Maidanski http://ivmai.chat.ru * Freeware class library sources. All rights reserved. ** * Language: Java [pure] * Tested with: JDK v1.1.6 * Last modified: 2000-12-14 10:35:00 GMT+03:00 */ /* * This software is the proprietary information of the author. ** * Permission to use, copy, and distribute this software and its * documentation for non-commercial purposes and without fee is * hereby granted provided that this copyright notice appears in all * copies. ** * This software should not be modified in any way; any found bug * should be reported to the author. ** * The author disclaims all warranties with regard to this software, * including all implied warranties of merchantability and fitness. * In no event shall the author be liable for any special, indirect * or consequential damages or any damages whatsoever resulting from * loss of use, data or profits, whether in an action of contract, * negligence or other tortuous action, arising out of or in * connection with the use or performance of this software. */ package ivmai.util; import java.io.Serializable; /** * Class for pseudo-random generator. ** * An instance of this class is used to generate a stream of * pseudo-random numbers. The class uses a 64-bit mutable seed, * which contains 61-bit and 3-bit shift registers (which stand for * ((x pow 61) + (x pow 3) + 1) and * ((y pow 3) + y + 1) factory polynomes). Each single * generated pseudo-random bit is ((x ^ y) & 1), where * new x is set to * (x * 2 | ((x >> 60) ^ (x >> 2)) & 1) and new * y is set to (y * 2 | ((y >> 2) ^ y) & 1), * which are the current non-zero values of the 61-bit and 3-bit * shift registers, respectively. Such construction of these two * shift registers guarantees good (but not cryptographically * strong) uniformly distributed pseudo-random bits sequence with * the aperiodity length of * (((2 pow 61) - 1) * ((2 pow 3) - 1)). Of course, the * actual algorithm is supplied with the fixes for zero values of * these shift registers (if x is zero then a non-zero * constant is added to this seed, if * y is zero then it is filled with the first non-zero * bits of x). The algorithm of this generator is * entirely implemented in nextInt(int) core method, * the others use it indirectly. The class also contains a method * for generation of normally distributed ('Gaussian') pseudo-random * numbers. ** * @see UnsignedInt * @see UnsignedLong * @see JavaConsts ** * @version 2.0 * @author Ivan Maidanski */ public class PseudoRandom implements ReallyCloneable, Serializable { /** * The class version unique identifier for serialization * interoperability. ** * @since 1.1 */ private static final long serialVersionUID = -1655238629402664209L; /** * Size of the first 'shift' register in seed * (61). ** * @see #GEN_B_SIZE * @see #seed * @see #nextInt(int) ** * @since 1.1 */ protected static final int GEN_A_SIZE = 61; /** * Size of the second 'shift' register in the lowest part of * seed (3). ** * @see #GEN_A_SIZE * @see #seed * @see #nextInt(int) ** * @since 1.1 */ protected static final int GEN_B_SIZE = 3; /** * The internal state associated with this * pseudo-random number generator. ** * seed (which consists of two shift registers) is * initially set by the constructor and modified each time * this generator produces a new value. seed * may be of any value, which may be modified asynchronously (since * the algorithm includes the checks for zero values of any or both * shift registers). ** * @serial ** * @see PseudoRandom#PseudoRandom(long) * @see #clone() * @see #nextInt(int) * @see #toString() */ protected long seed; /** * Creates a new pseudo-random generator with the predefined initial * state. ** * Initial state (seed) of generator is fully (but not * trivially) determined by initializer. Later, this * state is altered only by every (direct or indirect) call of * nextInt(int). Important notes: if two instances of * this class are created with the same value of * initializer, and the same sequence of method calls is * made for each, they will generate and return identical sequences * of numbers (and these instances will be equal); on the other * hand, if output reproducibility of a pseudo-random generator is * not required then it may be initialized on the current time. ** * @param initializer * the long value which fully determines the output pseudo-random * bits sequence of the created generator. ** * @see #nextInt(int) * @see #clone() * @see #equals(java.lang.Object) * @see #toString() */ public PseudoRandom(long initializer) { initializer = (initializer ^ 0x5DEECE66DL) * JavaConsts.LONG_GOLD_MEDIAN; initializer = (initializer >>> (GEN_A_SIZE - 1)) ^ (initializer << (GEN_B_SIZE + 1)); if (GEN_A_SIZE + GEN_B_SIZE < JavaConsts.LONG_SIZE) initializer &= ~(-1L << (GEN_A_SIZE + GEN_B_SIZE)); this.seed = initializer; } /** * Generates and returns the next uniformly distributed unsigned * int pseudo-random number according to the specified * maximum. ** * Returned value is drawn from the bits sequence of * this random number generator. The unsigned result is * uniformly distributed in the range from 0 to * unsignedMax, inclusive. All unsignedMax * plus one possible int values are produced with * (approximately) equal probability. The hedge 'approximately' is * used in the foregoing description only because the method is only * approximately an unbiased source of independently chosen bits. * This is a core method of the generator. This method alters state * of this generator. Important notes: synchronization * is not needed even outside (unless two or more threads use the * same pseudo-random generator constructed with some specified * initializer), since seed may be modified in an * asynchronous (even non-atomary) way by multiple threads; this * method may be overridden in the subclasses. ** * @param unsignedMax * the unsigned maximum on the random number to be returned. * @return * a pseudo-random, uniformly distributed unsigned int * value between 0 and unsignedMax * (inclusive). ** * @see #nextBits(int) * @see #nextLong(long) * @see #nextBytes(byte[], int, int) * @see #nextName(int) * @see #nextFloat() */ public int nextInt(int unsignedMax) { if (unsignedMax != 0) { int value; long seed; if (((seed = this.seed) & (GEN_A_SIZE + GEN_B_SIZE < JavaConsts.LONG_SIZE ? ~(-1L << GEN_A_SIZE) << GEN_B_SIZE : -1 << GEN_B_SIZE)) == 0L) seed += JavaConsts.LONG_GOLD_MEDIAN; if (((int)seed & ~(-1 << GEN_B_SIZE)) == 0) { long high = seed; do { high >>>= GEN_B_SIZE; } while ((value = (int)high & ~(-1 << GEN_B_SIZE)) == 0); seed |= value; } int max = unsignedMax; do { int result = 0; unsignedMax = max; do { value = (int)seed & ~(1 << GEN_B_SIZE); value ^= (value >> (GEN_B_SIZE - 1)) ^ (value << 1); if (GEN_A_SIZE + GEN_B_SIZE < JavaConsts.LONG_SIZE ? (seed & (1L << (GEN_A_SIZE + GEN_B_SIZE - 1))) != 0L : seed < 0L) value ^= 1 << GEN_B_SIZE; seed = (value & ((1 << GEN_B_SIZE) | 1)) ^ (seed << 1); result = ((value >> GEN_B_SIZE) ^ value) & 1 | (result << 1); } while ((unsignedMax >>>= 1) != 0); unsignedMax = result; } while (((~max | unsignedMax) & (max - unsignedMax)) < 0); if (GEN_A_SIZE + GEN_B_SIZE < JavaConsts.LONG_SIZE) seed &= ~(-1L << (GEN_A_SIZE + GEN_B_SIZE)); this.seed = seed; } return unsignedMax; } /** * Generates and returns the next uniformly distributed unsigned * long pseudo-random number according to the specified * maximum. ** * This method uses only nextInt(int) core method. The * unsigned result is uniformly distributed in the range from * 0 to unsignedMax, inclusive. All * unsignedMax plus one possible long values * are produced with (approximately) equal probability. In fact, * this is a secondary 'core' method. ** * @param unsignedMax * the unsigned maximum on the random number to be returned. * @return * a pseudo-random, uniformly distributed unsigned long * value between 0 and unsignedMax * (inclusive). ** * @see #nextInt(int) * @see #nextDouble() * @see #nextGaussian() */ public long nextLong(long unsignedMax) { int count, size = 0; long value = unsignedMax; while ((value & ~JavaConsts.INT_LMASK) != 0L) { value >>>= JavaConsts.INT_SIZE; size += JavaConsts.INT_SIZE; } if ((count = (int)value) != 0) for (size += JavaConsts.INT_SIZE; count > 0; count <<= 1, size--); do { value = 0L; count = size; while ((count -= JavaConsts.INT_SIZE) >= 0) value = nextInt(-1) & JavaConsts.INT_LMASK | (value << JavaConsts.INT_SIZE); if ((count += JavaConsts.INT_SIZE) > 0) value = nextInt(~(-1 << count)) | (value << count); } while (((~unsignedMax | value) & (unsignedMax - value)) < 0L); return value; } /** * Generates and returns the next pseudo-random bits sequence packed * into int value. ** * The resulting sequence is in lowest count bits of the * returned value (top bits are set to zero). Each bit of the * sequence may be 0 or 1 with the equal * probability. Negative count is treated as zero. If the * sequence is too long (to fit int value) then it is * truncated. This method uses only nextInt(int) * method. ** * @param count * the count of bits to be generated. * @return * a packed sequence of pseudo-random bits. ** * @see #nextInt(int) * @see #nextLongBits(int) * @see #nextBytes(byte[], int, int) ** * @since 1.1 */ public final int nextBits(int count) { int bits = 0; if (count > 0) { bits = -1; if (count < JavaConsts.INT_SIZE) bits = ~(-1 << count); bits = nextInt(bits); } return bits; } /** * Generates and returns the next pseudo-random bits sequence packed * into long value. ** * The resulting sequence is in lowest count bits of the * returned value (top bits are set to zero). Each bit of the * sequence may be 0 or 1 with the equal * probability. Negative count is treated as zero. If the * sequence is too long (to fit long value) then it is * truncated. This method uses only nextLong(long) * method. ** * @param count * the count of bits to be generated. * @return * a packed sequence of pseudo-random bits. ** * @see #nextLong(long) * @see #nextBits(int) * @see #nextBytes(byte[], int, int) ** * @since 1.1 */ public final long nextLongBits(int count) { long bits = 0L; if (count > 0) { bits = -1L; if (count < JavaConsts.LONG_SIZE) bits = ~(-1L << count); bits = nextLong(bits); } return bits; } /** * Generates pseudo-random bytes and places them into the supplied * byte array at the specified offset. ** * Each byte is uniformly distributed in all its range. Negative * len is treated as zero. If an exception is thrown then * generator state and bytes content remain unchanged. * This method uses only nextInt(int) core method. ** * @param bytes * the byte array (must be non-null and of enough * length) in which to put the generated pseudo-random bytes. * @param offset * the offset (in the supplied byte array) at which to put the * generated pseudo-random bytes. * @param len * the amount of pseudo-random bytes to generate. * @exception NullPointerException * if bytes is null. * @exception ArrayIndexOutOfBoundsException * if len is positive and (offset is negative * or is greater than length of bytes minus * len). ** * @see #nextInt(int) * @see #nextBits(int) * @see #nextLongBits(int) * @see #nextName(int) */ public void nextBytes(byte[] bytes, int offset, int len) throws NullPointerException, ArrayIndexOutOfBoundsException { int value = bytes.length; if (len > 0) { value = bytes[offset] | bytes[offset + len - 1]; do { bytes[offset++] = (byte)nextInt(JavaConsts.BYTE_MASK); } while (--len > 0); } } /** * Generates and returns the next pseudo-random (file) name. ** * This method is useful for generation of random names for * temporary files. The resulting string contains only uniformly * distributed characters from the set of '0' through '9' and 'a' * through 'z'. Negative len is treated as zero. If an * exception is thrown then generator state remains unchanged. This * method uses only nextInt(int) core method. ** * @param len * the amount of characters to generate. * @return * a string (not null, with length() of * max(len, 0)), which is just created and contains * only the pseudo-random characters from the set denoted above. * @exception OutOfMemoryError * if there is not enough memory. ** * @see #nextInt(int) * @see #nextBytes(byte[], int, int) ** * @since 2.0 */ public String nextName(int len) { if (len <= 0) len = 0; char[] chars = new char[len]; for (int value; len > 0; chars[--len] = (char)value) if ((value = nextInt(('9' - '0' + 1) + ('z' - 'a')) + '0') > '9') value += 'a' - '9' - 1; return new String(chars); } /** * Generates and returns the next uniformly distributed * float pseudo-random number in the range from * 0 (inclusive) to 1 (exclusive). ** * All possible floating-point values from the denoted range are * produced with (approximately) equal probability. This method uses * only nextInt(int) core method (to fill up the * mantissa of the floating-point value). ** * @return * a pseudo-random, uniformly distributed float value * between 0 (inclusive) and 1 * (exclusive). ** * @see #nextInt(int) * @see #nextDouble() * @see #nextGaussian() */ public final float nextFloat() { return (float)nextInt(JavaConsts.FLOAT_M_MASK) / (JavaConsts.FLOAT_M_MASK + 1); } /** * Generates and returns the next uniformly distributed * double pseudo-random number in the range from * 0 (inclusive) to 1 (exclusive). ** * All possible floating-point values from the denoted range are * produced with (approximately) equal probability. This method uses * only nextLong(long) method (to fill up the mantissa * of the floating-point value). ** * @return * a pseudo-random, uniformly distributed double value * between 0 (inclusive) and 1 * (exclusive). ** * @see #nextLong(long) * @see #nextFloat() * @see #nextGaussian() */ public final double nextDouble() { return (double)nextLong(JavaConsts.DOUBLE_M_MASK) / (JavaConsts.DOUBLE_M_MASK + 1L); } /** * Generates and returns the next normally distributed * double pseudo-random number. ** * Here, so called 'Polar Algorithm' is used to produce normally * distributed ('Gaussian') pseudo-random numbers with the standard * mean and deviation (mean 0 and deviation * 1). The method uses only nextLong(long) * method (to fill up the mantissa of the floating-point values), * and log(double), sqrt(double) functions * of Math class (to compute 'Gaussian' numbers). * Important notes: most of all the produced Gaussian numbers are in * the range from -6 to 6. ** * @return * a pseudo-random, normally distributed double value * with mean 0 and deviation 1. ** * @see #nextDouble() * @see #nextLong(long) */ public double nextGaussian() { double s, v, w; do { v = (double)nextLong(JavaConsts.DOUBLE_M_MASK) / ((JavaConsts.DOUBLE_M_MASK >>> 1) + 1L) - 1.0D; w = (double)nextLong(JavaConsts.DOUBLE_M_MASK) / ((JavaConsts.DOUBLE_M_MASK >>> 1) + 1L) - 1.0D; } while ((s = v * v + w * w) <= 0.0D || s >= 1.0D); s = Math.sqrt(-2.0D * Math.log(s) / s); return v * s; // (w * s) may be used as another independent result } /** * Creates and returns a copy of this object. ** * This method creates a new instance of the class of this object * and initializes its seed value with the same as * seed value of this object. ** * @return * a copy (not null and != this) of * this instance. * @exception OutOfMemoryError * if there is not enough memory. ** * @see PseudoRandom#PseudoRandom(long) * @see #equals(java.lang.Object) */ public Object clone() { Object obj; try { if ((obj = super.clone()) instanceof PseudoRandom && obj != this) return obj; } catch (CloneNotSupportedException e) {} throw new InternalError("CloneNotSupportedException"); } /** * Returns a hash code value for the object. ** * This method returns seed value 'squeezed' (hashed) to * value of int type. ** * @return * a hash code value for this object. ** * @see #equals(java.lang.Object) */ public int hashCode() { long seed = this.seed; return (int)((seed >> (JavaConsts.INT_SIZE - 1)) >> 1) ^ (int)seed; } /** * Indicates whether this object is equal to the * specified one. ** * This method returns true if and only if * obj is instance of this class and its seed * value is the same as seed value of this * object. ** * @param obj * the object (may be null) with which to compare. * @return * true if and only if this value is the * same as obj value. ** * @see PseudoRandom#PseudoRandom(long) * @see #clone() * @see #hashCode() */ public boolean equals(Object obj) { return obj == this || obj instanceof PseudoRandom && ((PseudoRandom)obj).seed == this.seed; } /** * Returns the string representation of the object. ** * This method returns the hexadecimal (with '0x' prefix) * zero-padded representation of seed. ** * @return * the string representation (not null, with non-zero * length()) of this object. * @exception OutOfMemoryError * if there is not enough memory. */ public String toString() { int digit, offset; long seed = this.seed; if (GEN_A_SIZE + GEN_B_SIZE < JavaConsts.LONG_SIZE) seed &= ~(-1L << (GEN_A_SIZE + GEN_B_SIZE)); char[] chars = new char[offset = ((GEN_A_SIZE + GEN_B_SIZE - 1) >> 2) + 3]; do { if ((digit = (int)seed & ((1 << 4) - 1)) > '9' - '0') digit += 'A' - '9' - 1; chars[--offset] = (char)(digit + '0'); seed >>>= 4; } while (offset > 2); chars[1] = 'x'; chars[0] = '0'; return new String(chars); } }