
src.it.unimi.dsi.util.SplitMix64RandomGenerator Maven / Gradle / Ivy
Show all versions of dsiutils Show documentation
package it.unimi.dsi.util;
/*
* DSI utilities
*
* Copyright (C) 2015-2016 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 it.unimi.dsi.logging.ProgressLogger;
import java.util.Random;
import org.apache.commons.math3.random.AbstractRandomGenerator;
import org.apache.commons.math3.random.RandomGenerator;
/** A non-splittable version of the SplitMix pseudorandom number generator used by Java 8's
* SplittableRandom
. Due to
* the fixed increment constant and to different strategies in generating finite ranges, the methods of this generator
* are faster than those of SplittableRandom
. Indeed, this is the fastest generator of the collection that
* passes the BigCrush battery of tests.
*
* @see it.unimi.dsi.util
* @see RandomGenerator
* @see SplitMix64Random
*/
public class SplitMix64RandomGenerator extends AbstractRandomGenerator {
/** 264 · φ, φ = (√5 − 1)/2. */
private static final long PHI = 0x9E3779B97F4A7C15L;
/** The internal state of the algorithm (a Weyl generator using the {@link #PHI} as increment). */
private long x;
/** Creates a new generator seeded using {@link Util#randomSeed()}. */
public SplitMix64RandomGenerator() {
this( Util.randomSeed() );
}
/** Creates a new generator using a given seed.
*
* @param seed a nonzero seed for the generator (if zero, the generator will be seeded with -1).
*/
public SplitMix64RandomGenerator( final long seed ) {
setSeed( seed );
}
/* David Stafford's (http://zimbry.blogspot.com/2011/09/better-bit-mixing-improving-on.html)
* "Mix13" variant of the 64-bit finalizer in Austin Appleby's MurmurHash3 algorithm. */
private static long staffordMix13( long z ) {
z = ( z ^ ( z >>> 30 ) ) * 0xBF58476D1CE4E5B9L;
z = ( z ^ ( z >>> 27 ) ) * 0x94D049BB133111EBL;
return z ^ ( z >>> 31 );
}
/* David Stafford's (http://zimbry.blogspot.com/2011/09/better-bit-mixing-improving-on.html)
* "Mix4" variant of the 64-bit finalizer in Austin Appleby's MurmurHash3 algorithm. */
private static int staffordMix4Upper32( long z ) {
z = ( z ^ ( z >>> 33 ) ) * 0x62A9D9ED799705F5L;
return (int)( ( ( z ^ ( z >>> 28 ) ) * 0xCB24D0A5C88C35B3L ) >>> 32 );
}
@Override
public long nextLong() {
return staffordMix13( x += PHI );
}
@Override
public int nextInt() {
return staffordMix4Upper32( x += PHI );
}
/** Returns a pseudorandom, approximately uniformly distributed {@code int} value
* between 0 (inclusive) and the specified value (exclusive), drawn from
* this random number generator's sequence.
*
* The hedge “approximately” is due to the fact that to be always
* faster than ThreadLocalRandom
* we return
* the upper 63 bits of {@link #nextLong()} modulo {@code n} instead of using
* {@link Random}'s fancy algorithm (which {@link #nextLong(long)} uses though).
* This choice introduces a bias: the numbers from 0 to 263 mod {@code n}
* are slightly more likely than the other ones. In the worst case, “more likely”
* means 1.00000000023 times more likely, which is in practice undetectable. If for some reason you
* need truly uniform generation, just use {@link #nextLong(long)}.
*
* @param n the positive bound on the random number to be returned.
* @return the next pseudorandom {@code int} value between {@code 0} (inclusive) and {@code n} (exclusive).
*/
@Override
public int nextInt( final int n ) {
if ( n <= 0 ) throw new IllegalArgumentException();
return (int)( ( staffordMix13( x += PHI ) >>> 1 ) % 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 = staffordMix13( x += PHI ) >>> 1;
final long value = bits % n;
if ( bits - value + ( n - 1 ) >= 0 ) return value;
}
}
@Override
public double nextDouble() {
return Double.longBitsToDouble( staffordMix13( x += PHI ) >>> 12 | 0x3FFL << 52 ) - 1.0;
}
@Override
public float nextFloat() {
return Float.intBitsToFloat( staffordMix4Upper32( x += PHI ) >>> 41 | 0x3F8 << 20 ) - 1.0f;
}
@Override
public boolean nextBoolean() {
return staffordMix4Upper32( x += PHI ) < 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 = staffordMix13( x += PHI ); n-- != 0; bits >>= 8 ) bytes[ --i ] = (byte)bits;
}
}
/** Sets the seed of this generator.
*
*
The seed will be passed through {@link HashCommon#murmurHash3(long)}.
*
* @param seed a seed for this generator.
*/
@Override
public void setSeed( final long seed ) {
x = HashCommon.murmurHash3( seed );
}
/** Sets the state of this generator.
*
* @param state the new state for this generator (must be nonzero).
*/
public void setState( final long state ) {
x = state;
}
public static void main( String[] arg ) {
long n = Long.parseLong( arg[ 0 ] );
long x = 0;
ProgressLogger pl = new ProgressLogger();
SplitMix64RandomGenerator r = new SplitMix64RandomGenerator();
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 );
}
}
}