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

src.it.unimi.dsi.util.XorShift1024StarPhiRandom Maven / Gradle / Ivy

package it.unimi.dsi.util;

/*
 * DSI utilities
 *
 * Copyright (C) 2013-2017 Sebastiano Vigna
 *
 *  This library is free software; you can redistribute it and/or modify it
 *  under the terms of the GNU Lesser General Public License as published by the Free
 *  Software Foundation; either version 3 of the License, or (at your option)
 *  any later version.
 *
 *  This library is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 *  or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 *  for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with this program; if not, see .
 *
 */


import it.unimi.dsi.Util;
import it.unimi.dsi.fastutil.HashCommon;

import java.util.Random;

/** A fast, high-quality {@linkplain Random pseudorandom number generator} that
 * combines a long-period instance of George Marsaglia's Xorshift generators (described in “Xorshift RNGs”, Journal of
 * Statistical Software, 8:1−6, 2003) with a multiplication.
 *
 * 

More details can be found in my paper “An experimental exploration of * Marsaglia's xorshift generators, scrambled”, ACM Trans. Math. Softw., 42(4), 2016, and * on the xoroshiro+/xorshift*/xorshift+ * generators and the PRNG shootout page. * *

With respect to the original XorShift1024StarRandom, this class offers * an improved constant that deletes linear dependencies from the third lowest bit. * *

Note that this is not a cryptographic-strength pseudorandom number generator. Its period is * 21024 − 1, which is more than enough for any massive parallel application (it is actually * possible to define analogously a generator with period 24096 − 1, * but its interest is eminently academic). * *

By using the supplied {@link #jump()} method it is possible to generate non-overlapping long sequences * for parallel computations. This class provides also a {@link #split()} method to support recursive parallel computations, in the spirit of * Java 8's SplittableRandom. * * @see it.unimi.dsi.util * @see Random * @see XorShift1024StarPhiRandomGenerator */ public class XorShift1024StarPhiRandom extends Random { private static final long serialVersionUID = 1L; /** The internal state of the algorithm. */ private long[] s; private int p; /** Creates a new generator seeded using {@link Util#randomSeed()}. */ public XorShift1024StarPhiRandom() { this(Util.randomSeed()); } /** Creates a new generator using a given seed. * * @param seed a seed for the generator. */ public XorShift1024StarPhiRandom(final long seed) { super(seed); } @Override protected int next(int bits) { return (int)(nextLong() >>> 64 - bits); } @Override public long nextLong() { final long s0 = s[p]; long s1 = s[p = (p + 1) & 15]; s1 ^= s1 << 31; return (s[p] = s1 ^ s0 ^ (s1 >>> 11) ^ (s0 >>> 30)) * 0x9e3779b97f4a7c13L; } @Override public int nextInt() { return (int)(nextLong() >>> 32); } @Override public int nextInt(final int n) { return (int)nextLong(n); } /** Returns a pseudorandom uniformly distributed {@code long} value * between 0 (inclusive) and the specified value (exclusive), drawn from * this random number generator's sequence. The algorithm used to generate * the value guarantees that the result is uniform, provided that the * sequence of 64-bit values produced by this generator is. * * @param n the positive bound on the random number to be returned. * @return the next pseudorandom {@code long} value between {@code 0} (inclusive) and {@code n} (exclusive). */ public long nextLong(final long n) { if (n <= 0) throw new IllegalArgumentException("illegal bound " + n + " (must be positive)"); long t = nextLong(); final long nMinus1 = n - 1; // Shortcut for powers of two--high bits if ((n & nMinus1) == 0) return t >>> Long.numberOfLeadingZeros(nMinus1); // Rejection-based algorithm to get uniform integers in the general case for (long u = t >>> 1; u + nMinus1 - (t = u % n) < 0; u = nextLong() >>> 1); return t; } @Override public double nextDouble() { return (nextLong() >>> 11) * 0x1.0p-53; } /** * Returns the next pseudorandom, uniformly distributed * {@code double} value between {@code 0.0} and * {@code 1.0} from this random number generator's sequence, * using a fast multiplication-free method which, however, * can provide only 52 significant bits. * *

This method is faster than {@link #nextDouble()}, but it * can return only dyadic rationals of the form k / 2−52, * instead of the standard k / 2−53. * *

The only difference between the output of this method and that of * {@link #nextDouble()} is an additional least significant bit set in half of the * returned values. For most applications, this difference is negligible. * * @return the next pseudorandom, uniformly distributed {@code double} * value between {@code 0.0} and {@code 1.0} from this * random number generator's sequence, using 52 significant bits only. */ public double nextDoubleFast() { return Double.longBitsToDouble(0x3FFL << 52 | nextLong() >>> 12) - 1.0; } @Override public float nextFloat() { return (nextLong() >>> 40) * 0x1.0p-24f; } @Override public boolean nextBoolean() { return nextLong() < 0; } @Override public void nextBytes(final byte[] bytes) { int i = bytes.length, n = 0; while(i != 0) { n = Math.min(i, 8); for (long bits = nextLong(); n-- != 0; bits >>= 8) bytes[--i] = (byte)bits; } } private static final long JUMP[] = { 0x84242f96eca9c41dL, 0xa3c65b8776f96855L, 0x5b34a39f070b5837L, 0x4489affce4f31a1eL, 0x2ffeeb0a48316f40L, 0xdc2d9891fe68c022L, 0x3659132bb12fea70L, 0xaac17d8efa43cab8L, 0xc4cb815590989b13L, 0x5ee975283d71c93bL, 0x691548c86c1bd540L, 0x7910c41d10a1e6a5L, 0x0b5fc64563b3e2a8L, 0x047f7684e9fc949dL, 0xb99181f2d8f685caL, 0x284600e3f30e38c3L }; /** The the jump function for this generator. It is equivalent to 2512 * calls to {@link #nextLong()}; it can be used to generate 2512 * non-overlapping subsequences for parallel computations. */ public void jump() { final long[] t = new long[16]; for (int i = 0; i < JUMP.length; i++) for (int b = 0; b < 64; b++) { if ((JUMP[i] & 1L << b) != 0) for (int j = 0; j < 16; j++) t[j] ^= s[(j + p) & 15]; nextLong(); } for (int j = 0; j < 16; j++) s[(j + p) & 15] = t[j]; } /** * Returns a new instance that shares no mutable state * with this instance. The sequence generated by the new instance * depends deterministically from the state of this instance, * but the probability that the sequence generated by this * instance and by the new instance overlap is negligible. * * @return the new instance. */ public XorShift1024StarPhiRandom split() { final XorShift1024StarPhiRandom split = new XorShift1024StarPhiRandom(); for(int i = s.length; i-- != 0;) split.s[i] = HashCommon.murmurHash3(s[i]); split.p = p; return split; } /** Sets the seed of this generator. * *

The argument will be used to seed a {@link SplitMix64RandomGenerator}, whose output * will in turn be used to seed this generator. This approach makes “warmup” unnecessary, * and makes the probability of starting from a state * with a large fraction of bits set to zero astronomically small. * * @param seed a seed for the generator. */ @Override public void setSeed(final long seed) { if (s == null) s = new long[16]; p = 0; final SplitMix64RandomGenerator r = new SplitMix64RandomGenerator(seed); for(int i = s.length; i-- != 0;) s[i] = r.nextLong(); } /** Sets the state of this generator. * *

The internal state of the generator will be reset, and the state array filled with the provided array. * * @param state an array of 16 longs; at least one must be nonzero. * @param p the internal index. */ public void setState(final long[] state, final int p) { if (state.length != s.length) throw new IllegalArgumentException("The argument array contains " + state.length + " longs instead of " + s.length); System.arraycopy(state, 0, s, 0, s.length); this.p = p; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy