View Javadoc
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  
19  import org.apache.commons.rng.core.util.NumberFactory;
20  
21  /**
22   * Port from Marsaglia's <a href="http://www.cse.yorku.ca/~oz/marsaglia-rng.html">
23   * "KISS" algorithm</a>.
24   * This version contains the correction referred to
25   * <a href="https://programmingpraxis.com/2010/10/05/george-marsaglias-random-number-generators/">here</a>
26   * in a reply to the original post.
27   *
28   * @see <a href="https://en.wikipedia.org/wiki/KISS_(algorithm)">KISS (Wikipedia)</a>
29   * @since 1.0
30   */
31  public class KISSRandom extends IntProvider {
32      /** Size of the seed. */
33      private static final int SEED_SIZE = 4;
34      /** State variable. */
35      private int z;
36      /** State variable. */
37      private int w;
38      /** State variable. */
39      private int jsr;
40      /** State variable. */
41      private int jcong;
42  
43      /**
44       * Creates a new instance.
45       *
46       * @param seed Seed.
47       * If the length is larger than 4, only the first 4 elements will
48       * be used; if smaller, the remaining elements will be automatically
49       * set.
50       */
51      public KISSRandom(int[] seed) {
52          setSeedInternal(seed);
53      }
54  
55      /** {@inheritDoc} */
56      @Override
57      protected byte[] getStateInternal() {
58          return composeStateInternal(NumberFactory.makeByteArray(new int[] {z, w, jsr, jcong}),
59                                      super.getStateInternal());
60      }
61  
62      /** {@inheritDoc} */
63      @Override
64      protected void setStateInternal(byte[] s) {
65          final byte[][] c = splitStateInternal(s, SEED_SIZE * 4);
66  
67          final int[] tmp = NumberFactory.makeIntArray(c[0]);
68          z = tmp[0];
69          w = tmp[1];
70          jsr = tmp[2];
71          jcong = tmp[3];
72  
73          super.setStateInternal(c[1]);
74      }
75  
76      /**
77       * Seeds the RNG.
78       *
79       * @param seed Seed.
80       */
81      private void setSeedInternal(int[] seed) {
82          // Reset the whole state of this RNG (i.e. the 4 state variables).
83          // Filling procedure is not part of the reference code.
84          final int[] tmp = new int[SEED_SIZE];
85          fillState(tmp, seed);
86  
87          z = tmp[0];
88          w = tmp[1];
89          jsr = tmp[2];
90          jcong = tmp[3];
91      }
92  
93      /** {@inheritDoc} */
94      @Override
95      public int next() {
96          z = computeNew(36969, z);
97          w = computeNew(18000, w);
98          final int mwc = (z << 16) + w;
99  
100         // Cf. correction mentioned in the reply to the original post:
101         //   https://programmingpraxis.com/2010/10/05/george-marsaglias-random-number-generators/
102         jsr ^= jsr << 13;
103         jsr ^= jsr >>> 17;
104         jsr ^= jsr << 5;
105 
106         jcong = 69069 * jcong + 1234567;
107 
108         return (mwc ^ jcong) + jsr;
109     }
110 
111     /**
112      * Compute new value.
113      *
114      * @param mult Multiplier.
115      * @param previous Previous value.
116      * @return new value.
117      */
118     private static int computeNew(int mult,
119                                   int previous) {
120         return mult * (previous & 65535) + (previous >>> 16);
121     }
122 }