KISSRandom.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.rng.core.source32;

  18. import org.apache.commons.rng.core.util.NumberFactory;

  19. /**
  20.  * Port from Marsaglia's <a href="http://www.cse.yorku.ca/~oz/marsaglia-rng.html">
  21.  * "KISS" algorithm</a>.
  22.  * This version contains the correction referred to
  23.  * <a href="https://programmingpraxis.com/2010/10/05/george-marsaglias-random-number-generators/">here</a>
  24.  * in a reply to the original post.
  25.  *
  26.  * @see <a href="https://en.wikipedia.org/wiki/KISS_(algorithm)">KISS (Wikipedia)</a>
  27.  * @since 1.0
  28.  */
  29. public class KISSRandom extends IntProvider {
  30.     /** Size of the seed. */
  31.     private static final int SEED_SIZE = 4;
  32.     /** State variable. */
  33.     private int z;
  34.     /** State variable. */
  35.     private int w;
  36.     /** State variable. */
  37.     private int jsr;
  38.     /** State variable. */
  39.     private int jcong;

  40.     /**
  41.      * Creates a new instance.
  42.      *
  43.      * @param seed Seed.
  44.      * If the length is larger than 4, only the first 4 elements will
  45.      * be used; if smaller, the remaining elements will be automatically
  46.      * set.
  47.      */
  48.     public KISSRandom(int[] seed) {
  49.         setSeedInternal(seed);
  50.     }

  51.     /** {@inheritDoc} */
  52.     @Override
  53.     protected byte[] getStateInternal() {
  54.         return composeStateInternal(NumberFactory.makeByteArray(new int[] {z, w, jsr, jcong}),
  55.                                     super.getStateInternal());
  56.     }

  57.     /** {@inheritDoc} */
  58.     @Override
  59.     protected void setStateInternal(byte[] s) {
  60.         final byte[][] c = splitStateInternal(s, SEED_SIZE * 4);

  61.         final int[] tmp = NumberFactory.makeIntArray(c[0]);
  62.         z = tmp[0];
  63.         w = tmp[1];
  64.         jsr = tmp[2];
  65.         jcong = tmp[3];

  66.         super.setStateInternal(c[1]);
  67.     }

  68.     /**
  69.      * Seeds the RNG.
  70.      *
  71.      * @param seed Seed.
  72.      */
  73.     private void setSeedInternal(int[] seed) {
  74.         // Reset the whole state of this RNG (i.e. the 4 state variables).
  75.         // Filling procedure is not part of the reference code.
  76.         final int[] tmp = new int[SEED_SIZE];
  77.         fillState(tmp, seed);

  78.         z = tmp[0];
  79.         w = tmp[1];
  80.         jsr = tmp[2];
  81.         jcong = tmp[3];
  82.     }

  83.     /** {@inheritDoc} */
  84.     @Override
  85.     public int next() {
  86.         z = computeNew(36969, z);
  87.         w = computeNew(18000, w);
  88.         final int mwc = (z << 16) + w;

  89.         // Cf. correction mentioned in the reply to the original post:
  90.         //   https://programmingpraxis.com/2010/10/05/george-marsaglias-random-number-generators/
  91.         jsr ^= jsr << 13;
  92.         jsr ^= jsr >>> 17;
  93.         jsr ^= jsr << 5;

  94.         jcong = 69069 * jcong + 1234567;

  95.         return (mwc ^ jcong) + jsr;
  96.     }

  97.     /**
  98.      * Compute new value.
  99.      *
  100.      * @param mult Multiplier.
  101.      * @param previous Previous value.
  102.      * @return new value.
  103.      */
  104.     private static int computeNew(int mult,
  105.                                   int previous) {
  106.         return mult * (previous & 65535) + (previous >>> 16);
  107.     }
  108. }