com.tarvon.fractala.util.Xorshift.java Source code

Java tutorial

Introduction

Here is the source code for com.tarvon.fractala.util.Xorshift.java

Source

/*
 * The MIT License (MIT)
 * 
 * Copyright (c) 2015-2016 saybur
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.tarvon.fractala.util;

import java.util.Objects;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;

import com.google.common.base.Stopwatch;

/**
 * Random number generator for voxel-based fractals based on the Xorshift
 * pseudo-random number generator described by George Marsaglia in his paper
 * <i>Xorshift RNGs</i> (<a href="http://www.jstatsoft.org/v08/i14/paper"
 * >http://www.jstatsoft.org/v08/i14/paper</a>).
 * <p>
 * This particular implementation has a period of <code>2 ^ 96 - 1</code> and
 * uses Marsaglia's triple of [13, 19, 3]. For use in cellular noise functions,
 * it has a method for resetting the generator state via
 * {@link Instance#setSeed(long, long, long)} for <code>x, y, z</code> voxel
 * coordinates, which will wrap on [0, 1024) for each of <code>x, y, z</code>.
 * <p>
 * Objects of <code>Xorshift.Instance</code> <i>are not</i> threadsafe.
 * Concurrent access should be externally synchronized or use separate
 * instances. <code>Xorshift</code> itself is threadsafe.
 * 
 * @author saybur
 *
 */
public final class Xorshift {
    /**
     * Random number generator instance for <code>Xorshift</code>. See the class
     * documentation for details.
     * <p>
     * A reminder: this is <i>not</i> threadsafe.
     * 
     * @author saybur
     *
     */
    public final class Instance {
        private long x, y, z;

        private Instance() {
            setSeed(0, 0, 0);
        }

        /**
         * Provides a pseudo-random <code>double</code> value on [0.0, 1.0).
         * 
         * @return the next double value.
         */
        public double nextDouble() {
            // get out of signed range, then divide in the remaining space
            // of the shifted long value.  i hope this works...
            return (nextLong() >>> 2) * (1.0 / (1L << 62));
        }

        /**
         * Provides a pseudo-random <code>long</code> value.
         * 
         * @return the next <code>long</code> value.
         */
        public long nextLong() {
            long t;
            t = (x ^ (x << 13));
            x = y;
            y = z;
            z = (z ^ (z >>> 3)) ^ (t ^ t >>> 19);
            return z;
        }

        /**
         * Sets the seed for this number generator to the given values.
         * <p>
         * This is for the cellular noise functions and may be ignored by other
         * users, as the constructor will initialize the generator with random
         * starting values.
         * 
         * @param x
         *            the X component.
         * @param y
         *            the Y component.
         * @param z
         *            the Z component.
         */
        public void setSeed(long x, long y, long z) {
            this.x = seeds[(int) (Math.abs(x) % SEED_TABLE_SPACE)];
            this.y = seeds[(int) (Math.abs(y) % SEED_TABLE_SPACE) + SEED_TABLE_SPACE];
            this.z = seeds[(int) (Math.abs(z) % SEED_TABLE_SPACE) + SEED_TABLE_SPACE + SEED_TABLE_SPACE];

            // decreases spherical artifacts, but obviously is slow
            // TODO replace by fixing underlying issue
            for (int i = 0; i < 5; i++)
                nextLong();
        }
    }

    /**
     * The space for each of x, y, and z in the seeds table.
     */
    private static final int SEED_TABLE_SPACE = 1024;
    /**
     * The final size of the seeds table.
     */
    private static final int SEED_TABLE_SIZE = SEED_TABLE_SPACE * 3;

    /**
     * Creates a new random number generator factory with a random seed value.
     * 
     * @return the random number generator factory.
     */
    public static Xorshift create() {
        return new Xorshift(ThreadLocalRandom.current().nextLong());
    }

    /**
     * Creates a new random number generator factory with the given seed value.
     * 
     * @param seed
     *            the seed value.
     * @return the random number generator factory.
     */
    public static Xorshift create(long seed) {
        return new Xorshift(seed);
    }

    private final long[] seeds;

    private Xorshift(long seed) {
        Random random = new Random(seed);
        seeds = new long[SEED_TABLE_SIZE];
        for (int i = 0; i < SEED_TABLE_SIZE; i++) {
            seeds[i] = random.nextLong();
        }
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Xorshift) {
            Xorshift o = (Xorshift) obj;
            return Objects.equals(seeds, o.seeds);
        } else {
            return false;
        }
    }

    /**
     * Creates a new random number generator instance.
     * <p>
     * Each instance should be used by a single thread only. See the class
     * documentation for information about the generator itself.
     * 
     * @return a new random number generator instance.
     */
    public Xorshift.Instance getInstance() {
        return new Xorshift.Instance();
    }

    @Override
    public int hashCode() {
        return Objects.hash(seeds);
    }

    /**
     * Main method used for simple testing of the PRNG.
     * 
     * @param args ignored arguments.
     */
    public static void main(String... args) {
        Xorshift xorshift = Xorshift.create(0);
        Xorshift.Instance r = xorshift.getInstance();

        Stopwatch sw = Stopwatch.createStarted();
        double min = Double.MAX_VALUE;
        double max = Double.MIN_VALUE;
        for (int i = 0; i < Integer.MAX_VALUE; i++) {
            //long v = r.nextLong();
            //double d = Double.longBitsToDouble(v);
            double d = r.nextDouble();
            if (d < min)
                min = d;
            if (d > max)
                max = d;
        }
        System.out.printf("Making %d doubles took %s\n", Integer.MAX_VALUE, sw);
        System.out.printf("min double: %1.40f\n", min);
        System.out.printf("max double: %1.40f\n", max);

        // histogram to see if there is really bad clustering
        // on the doubles generator
        int[] bins = new int[100];
        for (int i = 0; i < Integer.MAX_VALUE; i++) {
            double d = r.nextDouble();
            int b = (int) (d * 100);
            bins[b]++;
        }
        System.out.println("Histogram of doubles:");
        for (int i = 0; i < 100; i++) {
            System.out.printf("bin %d:\t%d\n", i, bins[i]);
        }
    }
}