1 /++ 2 Linear Congruential generator. 3 4 Copyright: Copyright Andrei Alexandrescu 2008 - 2009, Ilya Yaroshenko 2016-. 5 License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). 6 Authors: $(HTTP erdani.org, Andrei Alexandrescu) Ilya Yaroshenko (rework) 7 +/ 8 module mir.random.engine.linear_congruential; 9 10 import std.traits; 11 12 /++ 13 Linear Congruential generator. 14 +/ 15 struct LinearCongruentialEngine(Uint, Uint a, Uint c, Uint m) 16 if (isUnsigned!Uint) 17 { 18 /// 19 enum isRandomEngine = true; 20 21 /// Highest generated value (`modulus - 1 - bool(c == 0)`). 22 enum Uint max = m - 1 - bool(c == 0); 23 /** 24 The parameters of this distribution. The random number is $(D_PARAM x 25 = (x * multiplier + increment) % modulus). 26 */ 27 enum Uint multiplier = a; 28 ///ditto 29 enum Uint increment = c; 30 ///ditto 31 enum Uint modulus = m; 32 33 static assert(m == 0 || a < m); 34 static assert(m == 0 || c < m); 35 static assert(m == 0 || (cast(ulong)a * (m-1) + c) % m == (c < a ? c - a + m : c - a)); 36 37 /++ 38 The low bits of a linear congruential generator whose modulus is a 39 power of 2 have a much shorter period than the high bits. 40 Note that for LinearCongruentialEngine, `modulus == 0` signifies 41 a modulus of `2 ^^ (Uint.sizeof*8)` which is not representable as `Uint`. 42 +/ 43 enum bool preferHighBits = 0 == (modulus & (modulus - 1)); 44 45 @disable this(); 46 @disable this(this); 47 48 // Check for maximum range 49 private static ulong gcd()(ulong a, ulong b) 50 { 51 while (b) 52 { 53 auto t = b; 54 b = a % b; 55 a = t; 56 } 57 return a; 58 } 59 60 private static ulong primeFactorsOnly()(ulong n) 61 { 62 ulong result = 1; 63 ulong iter = 2; 64 for (; n >= iter * iter; iter += 2 - (iter == 2)) 65 { 66 if (n % iter) continue; 67 result *= iter; 68 do 69 { 70 n /= iter; 71 } while (n % iter == 0); 72 } 73 return result * n; 74 } 75 76 @safe pure nothrow version(mir_random_test) unittest 77 { 78 static assert(primeFactorsOnly(100) == 10); 79 static assert(primeFactorsOnly(11) == 11); 80 static assert(primeFactorsOnly(7 * 7 * 7 * 11 * 15 * 11) == 7 * 11 * 15); 81 static assert(primeFactorsOnly(129 * 2) == 129 * 2); 82 // enum x = primeFactorsOnly(7 * 7 * 7 * 11 * 15); 83 // static assert(x == 7 * 11 * 15); 84 } 85 86 private static bool properLinearCongruentialParameters()(ulong m,ulong a, ulong c) 87 { 88 if (m == 0) 89 { 90 static if (is(Uint == uint)) 91 { 92 // Assume m is uint.max + 1 93 m = (1uL << 32); 94 } 95 else 96 { 97 return false; 98 } 99 } 100 // Bounds checking 101 if (a == 0 || a >= m || c >= m) return false; 102 // c and m are relatively prime 103 if (c > 0 && gcd(c, m) != 1) return false; 104 // a - 1 is divisible by all prime factors of m 105 if ((a - 1) % primeFactorsOnly(m)) return false; 106 // if a - 1 is multiple of 4, then m is a multiple of 4 too. 107 if ((a - 1) % 4 == 0 && m % 4) return false; 108 // Passed all tests 109 return true; 110 } 111 112 // check here 113 static assert(c == 0 || properLinearCongruentialParameters(m, a, c), 114 "Incorrect instantiation of LinearCongruentialEngine"); 115 116 /** 117 Constructs a $(D_PARAM LinearCongruentialEngine) generator seeded with 118 `x0`. 119 Params: 120 x0 = seed, must be positive if c equals to 0. 121 */ 122 this(Uint x0) @safe pure nothrow @nogc 123 { 124 _x = modulus ? (x0 % modulus) : x0; 125 static if (c == 0) 126 { 127 //Necessary to prevent generator from outputting an endless series of zeroes. 128 if (_x == 0) 129 _x = max; 130 } 131 } 132 133 /** 134 Advances the random sequence. 135 */ 136 Uint opCall() @safe pure nothrow @nogc 137 { 138 static if (m) 139 { 140 static if (is(Uint == uint)) 141 { 142 static if (m == uint.max) 143 { 144 immutable ulong 145 x = (cast(ulong) a * _x + c), 146 v = x >> 32, 147 w = x & uint.max; 148 immutable y = cast(uint)(v + w); 149 _x = (y < v || y == uint.max) ? (y + 1) : y; 150 } 151 else static if (m == int.max) 152 { 153 immutable ulong 154 x = (cast(ulong) a * _x + c), 155 v = x >> 31, 156 w = x & int.max; 157 immutable uint y = cast(uint)(v + w); 158 _x = (y >= int.max) ? (y - int.max) : y; 159 } 160 else 161 { 162 _x = cast(uint) ((cast(ulong) a * _x + c) % m); 163 } 164 } 165 else static assert(0); 166 } 167 else 168 { 169 _x = a * _x + c; 170 } 171 static if (c == 0) 172 return _x - 1; 173 else 174 return _x; 175 } 176 177 private Uint _x; 178 } 179 180 /** 181 Define $(D_PARAM LinearCongruentialEngine) generators with well-chosen 182 parameters. `MinstdRand0` implements Park and Miller's "minimal 183 standard" $(HTTP 184 wikipedia.org/wiki/Park%E2%80%93Miller_random_number_generator, 185 generator) that uses 16807 for the multiplier. `MinstdRand` 186 implements a variant that has slightly better spectral behavior by 187 using the multiplier 48271. Both generators are rather simplistic. 188 */ 189 alias MinstdRand0 = LinearCongruentialEngine!(uint, 16807, 0, 2147483647); 190 /// ditto 191 alias MinstdRand = LinearCongruentialEngine!(uint, 48271, 0, 2147483647); 192 193 /// 194 @safe version(mir_random_test) unittest 195 { 196 import mir.random.engine; 197 // seed with a constant 198 auto rnd0 = MinstdRand0(1); 199 auto n = rnd0(); // same for each run 200 // Seed with an unpredictable value 201 rnd0 = MinstdRand0(cast(uint)unpredictableSeed); 202 n = rnd0(); // different across runs 203 204 import std.traits; 205 static assert(is(ReturnType!rnd0 == uint)); 206 } 207 208 @safe version(mir_random_test) unittest 209 { 210 auto rnd0 = MinstdRand0(MinstdRand0.modulus); 211 auto n = rnd0(); 212 assert(n != rnd0()); 213 } 214 215 version(mir_random_test) unittest 216 { 217 import mir.random.engine; 218 static assert(isRandomEngine!MinstdRand); 219 static assert(isRandomEngine!MinstdRand0); 220 221 static assert(!isSaturatedRandomEngine!MinstdRand); 222 static assert(!isSaturatedRandomEngine!MinstdRand0); 223 224 // The correct numbers are taken from The Database of Integer Sequences 225 // http://www.research.att.com/~njas/sequences/eisBTfry00128.txt 226 auto checking0 = [ 227 16807,282475249,1622650073,984943658,1144108930,470211272, 228 101027544,1457850878,1458777923,2007237709,823564440,1115438165, 229 1784484492,74243042,114807987,1137522503,1441282327,16531729, 230 823378840,143542612 ]; 231 232 auto rnd0 = MinstdRand0(1); 233 234 foreach (e; checking0) 235 { 236 assert(rnd0() == e - 1); 237 } 238 // Test the 10000th invocation 239 // Correct value taken from: 240 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2461.pdf 241 rnd0 = MinstdRand0(1); 242 foreach(_; 0 .. 9999) 243 rnd0(); 244 assert(rnd0() == 1043618065 - 1); 245 246 // Test MinstdRand 247 auto checking = [48271UL,182605794,1291394886,1914720637,2078669041, 248 407355683]; 249 auto rnd = MinstdRand(1); 250 foreach (e; checking) 251 { 252 assert(rnd() == e - 1); 253 } 254 255 // Test the 10000th invocation 256 // Correct value taken from: 257 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2461.pdf 258 rnd = MinstdRand(1); 259 foreach(_; 0 .. 9999) 260 rnd(); 261 assert(rnd() == 399268537 - 1); 262 }