src.it.unimi.dsi.util.XoShiRo256PlusRandomGenerator Maven / Gradle / Ivy
Show all versions of dsiutils Show documentation
package it.unimi.dsi.util;
/*
* DSI utilities
*
* Copyright (C) 2013-2018 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 java.io.Serializable;
import java.security.SecureRandom;
import java.util.SplittableRandom;
import org.apache.commons.math3.random.AbstractRandomGenerator;
import org.apache.commons.math3.random.RandomGenerator;
import it.unimi.dsi.Util;
import it.unimi.dsi.fastutil.HashCommon;
import it.unimi.dsi.logging.ProgressLogger;
/** A fast, rock-solid {@linkplain RandomGenerator pseudorandom number generator} for floating-point generation.
* It has excellent speed, a state space (256 bits) that is large enough for
* any parallel application, and it is faster than {@link XoShiRo256StarStarRandomGenerator}. It passes all
* tests we are aware of except for the lowest three bits, which might
* fail linearity tests (and just those), so if low linear complexity is
* not considered an issue (as it is usually the case) it can be used to
* generate integer outputs, too.
* More information can be found at our PRNG page.
*
* If you need a general PRNG, use {@link XoShiRo256StarStarRandomGenerator}. If you are tight on space,
* you might try {@link XoRoShiRo128PlusRandomGenerator}.
*
*
By using the supplied {@link #jump()} method it is possible to generate non-overlapping long sequences
* for parallel computations; {@link #longJump()} makes it possible to create several
* starting points, each providing several non-overlapping sequences, for distributed computations. This class provides also a {@link #split()} method to support recursive parallel computations, in the spirit of
* {@link SplittableRandom}.
*
*
Note that this is not a {@linkplain SecureRandom secure generator}.
*
* @version 1.0
* @see it.unimi.dsi.util
* @see RandomGenerator
* @see XoShiRo256PlusRandom
*/
@SuppressWarnings("javadoc")
public class XoShiRo256PlusRandomGenerator extends AbstractRandomGenerator implements Serializable {
private static final long serialVersionUID = 0L;
/** The internal state of the algorithm. */
private long s0, s1, s2, s3;
protected XoShiRo256PlusRandomGenerator(final long s0, final long s1, final long s2, final long s3) {
this.s0 = s0;
this.s1 = s1;
this.s2 = s2;
this.s3 = s3;
}
/** Creates a new generator seeded using {@link Util#randomSeed()}. */
public XoShiRo256PlusRandomGenerator() {
this(Util.randomSeed());
}
/** Creates a new generator using a given seed.
*
* @param seed a seed for the generator.
*/
public XoShiRo256PlusRandomGenerator(final long seed) {
setSeed(seed);
}
/** Returns a copy of this generator. The sequences produced by this generator and by the returned generator will be identical.
*
*
This method is particularly useful in conjunction with the {@link #jump()} method: by calling repeatedly
* {@link #jump() jump().copy()} over a generator it is possible to create several generators producing non-overlapping sequences.
*
* @return a copy of this generator.
*/
public XoShiRo256PlusRandomGenerator copy() {
return new XoShiRo256PlusRandomGenerator(s0, s1, s2, s3);
}
@Override
public long nextLong() {
final long result = s0 + s3;
final long t = s1 << 17;
s2 ^= s0;
s3 ^= s1;
s1 ^= s2;
s0 ^= s3;
s2 ^= t;
s3 = Long.rotateLeft(s3, 45);
return result;
}
@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)) & 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. Before
* version 2.4.1, this was actually the standard implementation of
* {@link #nextDouble()}, so you can use this method if you need to
* reproduce exactly results obtained using previous versions.
*
*
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[] = { 0x180ec6d33cfd0abaL, 0xd5a61266f0c9392cL, 0xa9582618e03fc9aaL, 0x39abdc4529b1661cL };
private XoShiRo256PlusRandomGenerator jump(final long[] jump) {
long s0 = 0;
long s1 = 0;
long s2 = 0;
long s3 = 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;
s2 ^= this.s2;
s3 ^= this.s3;
}
nextLong();
}
this.s0 = s0;
this.s1 = s1;
this.s2 = s2;
this.s3 = s3;
return this;
}
/** The jump function for this generator. It is equivalent to 2128
* calls to {@link #nextLong()}; it can be used to generate 2128
* non-overlapping subsequences for parallel computations.
*
* @return this generator.
* @see #copy()
*/
public XoShiRo256PlusRandomGenerator jump() {
return jump(JUMP);
}
private static final long LONG_JUMP[] = { 0x76e15d3efefdcbbfL, 0xc5004e441c522fb3L, 0x77710069854ee241L, 0x39109bb02acbe635L };
/** The long-jump function for this generator. It is equivalent to 2192
* calls to {@link #nextLong()}; it can be used to generate 264 starting points,
* from each of which {@link #jump()} will generate 264 non-overlapping
* subsequences for parallel distributed computations.
*
* @return this generator.
* @see #copy()
*/
public XoShiRo256PlusRandomGenerator longJump() {
return jump(LONG_JUMP);
}
/**
* 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 XoShiRo256PlusRandomGenerator split() {
final XoShiRo256PlusRandomGenerator split = new XoShiRo256PlusRandomGenerator();
split.s0 = HashCommon.murmurHash3(s0);
split.s1 = HashCommon.murmurHash3(s1);
split.s2 = HashCommon.murmurHash3(s2);
split.s3 = HashCommon.murmurHash3(s3);
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();
s2 = r.nextLong();
s3 = 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 != 4) throw new IllegalArgumentException("The argument array contains " + state.length + " longs instead of " + 2);
s0 = state[0];
s1 = state[1];
s2 = state[2];
s3 = state[3];
}
public static void main(final String[] arg) {
final long n = Long.parseLong(arg[0]);
long x = 0;
final ProgressLogger pl = new ProgressLogger();
final XoShiRo256PlusRandomGenerator r = new XoShiRo256PlusRandomGenerator(0);
for(int k = 10; k-- != 0;) {
pl.start("Measuring...");
for (long i = n; i-- != 0;)
x ^= r.nextLong();
pl.done(n);
if (x == 0) System.out.println(x);
}
}
}