src.it.unimi.dsi.util.XorShift128PlusRandom 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
* returns the sum of consecutive outputs of a Marsaglia Xorshift generator (described in “Xorshift RNGs”, Journal of
* Statistical Software, 8:1−6, 2003) with 128 bits of state.
* It is presently used in the JavaScript engines of
* Chrome,
* Firefox,
* Safari and Edge.
*
* Warning: this generator has been superseded by {@link XoRoShiRo128PlusRandom},
* which is faster and has stronger statistical properties.
*
*
More details can be found in my paper “Further scramblings
* of Marsaglia's xorshift
generators”, Journal of Computational and Applied Mathematics, 315:175−181, 2016, and
* on the xoroshiro+
/xorshift*
/xorshift+
* generators and the PRNG shootout page.
* The basic idea is taken from Mutsuo Saito and Makuto Matsumoto's
* XSadd
generator, which
* is however based on 32-bit shifts and fails several statistical tests when reversed.
*
*
Note that this is
* not a cryptographic-strength pseudorandom number generator, but its quality is
* preposterously higher than {@link Random}'s, and its cycle length is
* 2128 − 1, which is more than enough for any single-thread application.
*
*
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 XoRoShiRo128PlusRandom
* @see XorShift128PlusRandomGenerator
* @deprecated Please use {@link XoRoShiRo128PlusRandom} instead.
*/
@Deprecated
public class XorShift128PlusRandom extends Random {
private static final long serialVersionUID = 1L;
/** The internal state of the algorithm. */
private long s0, s1;
/** Creates a new generator seeded using {@link Util#randomSeed()}. */
public XorShift128PlusRandom() {
this(Util.randomSeed());
}
/** Creates a new generator using a given seed.
*
* @param seed a seed for the generator.
*/
public XorShift128PlusRandom(final long seed) {
setSeed(seed);
}
@Override
public long nextLong() {
long s1 = this.s0;
final long s0 = this.s1;
this.s0 = s0;
s1 ^= s1 << 23;
return (this.s1 = (s1 ^ s0 ^ (s1 >>> 18) ^ (s0 >>> 5))) + s0;
}
@Override
public int nextInt() {
return (int)nextLong();
}
@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();
// No special provision for n power of two: all our bits are good.
for(;;) {
final long bits = nextLong() >>> 1;
final long value = bits % n;
if (bits - value + (n - 1) >= 0) return value;
}
}
@Override
public double nextDouble() {
return (nextLong() >>> 11) * 0x1.0p-53;
}
@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[] = { 0x8a5cd789635d2dffL, 0x121fd2155c472f96L };
/** The the jump function for this generator. It is equivalent to 264
* calls to {@link #nextLong()}; it can be used to generate 264
* non-overlapping subsequences for parallel computations. */
public void jump() {
long s0 = 0;
long s1 = 0;
for(int i = 0; i < JUMP.length; i++)
for(int b = 0; b < 64; b++) {
if ((JUMP[i] & 1L << b) != 0) {
s0 ^= this.s0;
s1 ^= this.s1;
}
nextLong();
}
this.s0 = s0;
this.s1 = s1;
}
/**
* 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 XorShift128PlusRandom split() {
final XorShift128PlusRandom split = new XorShift128PlusRandom();
split.s0 = HashCommon.murmurHash3(s0);
split.s1 = HashCommon.murmurHash3(s1);
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 this generator.
*/
@Override
public void setSeed(final long seed) {
final SplitMix64RandomGenerator r = new SplitMix64RandomGenerator(seed);
s0 = r.nextLong();
s1 = 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 2 longs; at least one must be nonzero.
*/
public void setState(final long[] state) {
if (state.length != 2) throw new IllegalArgumentException("The argument array contains " + state.length + " longs instead of " + 2);
s0 = state[0];
s1 = state[1];
}
}