MiddleSquareWeylSequence.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. import java.util.Arrays;

  20. /**
  21.  * Middle Square Weyl Sequence Random Number Generator.
  22.  *
  23.  * <p>A fast all-purpose 32-bit generator. Memory footprint is 192 bits and the period is at least
  24.  * {@code 2^64}.</p>
  25.  *
  26.  * <p>Implementation is based on the paper
  27.  * <a href="https://arxiv.org/abs/1704.00358v3">Middle Square Weyl Sequence RNG</a>.</p>
  28.  *
  29.  * @see <a href="https://en.wikipedia.org/wiki/Middle-square_method">Middle Square Method</a>
  30.  * @since 1.3
  31.  */
  32. public class MiddleSquareWeylSequence extends IntProvider {
  33.     /** Size of the seed array. */
  34.     private static final int SEED_SIZE = 3;
  35.     /**
  36.      * The default seed.
  37.      * This has a high quality Weyl increment (containing many bit state transitions).
  38.      */
  39.     private static final long[] DEFAULT_SEED =
  40.         {0x012de1babb3c4104L, 0xc8161b4202294965L, 0xb5ad4eceda1ce2a9L};

  41.     /** State of the generator. */
  42.     private long x;
  43.     /** State of the Weyl sequence. */
  44.     private long w;
  45.     /**
  46.      * Increment for the Weyl sequence. This must be odd to ensure a full period.
  47.      *
  48.      * <p>This is not final to support the restore functionality.</p>
  49.      */
  50.     private long s;

  51.     /**
  52.      * Creates a new instance.
  53.      *
  54.      * <p>Note: The generator output quality is highly dependent on the initial seed.
  55.      * If the generator is seeded poorly (for example with all zeros) it is possible the
  56.      * generator may output zero for many cycles before the internal state recovers randomness.
  57.      * The seed elements are used to set:</p>
  58.      *
  59.      * <ol>
  60.      *   <li>The state of the generator
  61.      *   <li>The state of the Weyl sequence
  62.      *   <li>The increment of the Weyl sequence
  63.      * </ol>
  64.      *
  65.      * <p>The third element is set to odd to ensure a period of at least 2<sup>64</sup>. If the
  66.      * increment is of low complexity then the Weyl sequence does not contribute high quality
  67.      * randomness. It is recommended to use a permutation of 8 hex characters for the upper
  68.      * and lower 32-bits of the increment.</p>
  69.      *
  70.      * <p>The state of the generator is squared during each cycle. There is a possibility that
  71.      * different seeds can produce the same output, for example 0 and 2<sup>32</sup> produce
  72.      * the same square. This can be avoided by using the high complexity Weyl increment for the
  73.      * state seed element.</p>
  74.      *
  75.      * @param seed Initial seed.
  76.      * If the length is larger than 3, only the first 3 elements will
  77.      * be used; if smaller, the remaining elements will be automatically set.
  78.      */
  79.     public MiddleSquareWeylSequence(long[] seed) {
  80.         if (seed.length < SEED_SIZE) {
  81.             // Complete the seed with a default to avoid
  82.             // low complexity Weyl increments.
  83.             final long[] tmp = Arrays.copyOf(seed, SEED_SIZE);
  84.             System.arraycopy(DEFAULT_SEED, seed.length, tmp, seed.length, SEED_SIZE - seed.length);
  85.             setSeedInternal(tmp);
  86.         } else {
  87.             setSeedInternal(seed);
  88.         }
  89.     }

  90.     /**
  91.      * Seeds the RNG.
  92.      *
  93.      * @param seed Seed.
  94.      */
  95.     private void setSeedInternal(long[] seed) {
  96.         x = seed[0];
  97.         w = seed[1];
  98.         // Ensure the increment is odd to provide a maximal period Weyl sequence.
  99.         this.s = seed[2] | 1L;
  100.     }

  101.     /** {@inheritDoc} */
  102.     @Override
  103.     protected byte[] getStateInternal() {
  104.         return composeStateInternal(NumberFactory.makeByteArray(new long[] {x, w, s}),
  105.                                     super.getStateInternal());
  106.     }

  107.     /** {@inheritDoc} */
  108.     @Override
  109.     protected void setStateInternal(byte[] state) {
  110.         final byte[][] c = splitStateInternal(state, SEED_SIZE * 8);
  111.         setSeedInternal(NumberFactory.makeLongArray(c[0]));
  112.         super.setStateInternal(c[1]);
  113.     }

  114.     /** {@inheritDoc} */
  115.     @Override
  116.     public int next() {
  117.         x *= x;
  118.         x += w += s;
  119.         x = (x >>> 32) | (x << 32);
  120.         return (int) x;
  121.     }

  122.     /** {@inheritDoc} */
  123.     @Override
  124.     public long nextLong() {
  125.         // Avoid round trip from long to int to long by performing two iterations inline
  126.         x *= x;
  127.         x += w += s;
  128.         final long i1 = x & 0xffffffff00000000L;
  129.         x = (x >>> 32) | (x << 32);
  130.         x *= x;
  131.         x += w += s;
  132.         final long i2 = x >>> 32;
  133.         x = i2 | x << 32;
  134.         return i1 | i2;
  135.     }
  136. }