1 /******************************************************************************* 2 3 copyright: Copyright (c) 2008. All rights reserved 4 5 license: BSD style: $(LICENSE) 6 7 version: Initial release: May 2008 8 9 author: Various 10 11 Since: 0.99.7 12 13 With gratitude to Dr Jurgen A Doornik. See his paper entitled 14 "Conversion of high-period random numbers to floating point" 15 16 *******************************************************************************/ 17 18 module tango.math.random.Kiss; 19 20 21 version (Win32) 22 private extern(Windows) int QueryPerformanceCounter (ulong *); 23 24 version (Posix) 25 { 26 private import tango.stdc.posix.sys.time; 27 } 28 29 30 /****************************************************************************** 31 32 KISS (from George Marsaglia) 33 34 The idea is to use simple, fast, individually promising 35 generators to get a composite that will be fast, easy to code 36 have a very long period and pass all the tests put to it. 37 The three components of KISS are 38 --- 39 x(n)=a*x(n-1)+1 mod 2^32 40 y(n)=y(n-1)(I+L^13)(I+R^17)(I+L^5), 41 z(n)=2*z(n-1)+z(n-2) +carry mod 2^32 42 --- 43 44 The y's are a shift register sequence on 32bit binary vectors 45 period 2^32-1; The z's are a simple multiply-with-carry sequence 46 with period 2^63+2^32-1. The period of KISS is thus 47 --- 48 2^32*(2^32-1)*(2^63+2^32-1) > 2^127 49 --- 50 51 Note that this should be passed by reference, unless you really 52 intend to provide a local copy to a callee 53 54 ******************************************************************************/ 55 56 struct Kiss 57 { 58 /// 59 public alias natural toInt; 60 /// 61 public alias fraction toReal; 62 63 private uint kiss_k; 64 private uint kiss_m; 65 private uint kiss_x = 1; 66 private uint kiss_y = 2; 67 private uint kiss_z = 4; 68 private uint kiss_w = 8; 69 private uint kiss_carry = 0; 70 71 private enum double M_RAN_INVM32 = 2.32830643653869628906e-010, 72 M_RAN_INVM52 = 2.22044604925031308085e-016; 73 74 /********************************************************************** 75 76 A global, shared instance, seeded via startup time 77 78 **********************************************************************/ 79 80 public static __gshared Kiss instance; 81 82 shared static this () 83 { 84 instance.seed(); 85 } 86 87 /********************************************************************** 88 89 Creates and seeds a new generator with the current time 90 91 **********************************************************************/ 92 93 static Kiss opCall () 94 { 95 Kiss rand; 96 rand.seed(); 97 return rand; 98 } 99 100 /********************************************************************** 101 102 Seed the generator with current time 103 104 **********************************************************************/ 105 106 void seed () 107 { 108 ulong s; 109 110 version (Posix) 111 { 112 timeval tv; 113 114 gettimeofday (&tv, null); 115 s = tv.tv_usec; 116 } 117 version (Win32) 118 QueryPerformanceCounter (&s); 119 120 return seed (cast(uint) s); 121 } 122 123 /********************************************************************** 124 125 Seed the generator with a provided value 126 127 **********************************************************************/ 128 129 void seed (uint seed) 130 { 131 kiss_x = seed | 1; 132 kiss_y = seed | 2; 133 kiss_z = seed | 4; 134 kiss_w = seed | 8; 135 kiss_carry = 0; 136 } 137 138 /********************************************************************** 139 140 Returns X such that 0 <= X <= uint.max 141 142 **********************************************************************/ 143 144 uint natural () 145 { 146 kiss_x = kiss_x * 69069 + 1; 147 kiss_y ^= kiss_y << 13; 148 kiss_y ^= kiss_y >> 17; 149 kiss_y ^= kiss_y << 5; 150 kiss_k = (kiss_z >> 2) + (kiss_w >> 3) + (kiss_carry >> 2); 151 kiss_m = kiss_w + kiss_w + kiss_z + kiss_carry; 152 kiss_z = kiss_w; 153 kiss_w = kiss_m; 154 kiss_carry = kiss_k >> 30; 155 return kiss_x + kiss_y + kiss_w; 156 } 157 158 /********************************************************************** 159 160 Returns X such that 0 <= X < max 161 162 Note that max is exclusive, making it compatible with 163 array indexing 164 165 **********************************************************************/ 166 167 uint natural (uint max) 168 { 169 return natural() % max; 170 } 171 172 /********************************************************************** 173 174 Returns X such that min <= X < max 175 176 Note that max is exclusive, making it compatible with 177 array indexing 178 179 **********************************************************************/ 180 181 uint natural (uint min, uint max) 182 { 183 return (natural() % (max-min)) + min; 184 } 185 186 /********************************************************************** 187 188 Returns a value in the range [0, 1) using 32 bits 189 of precision (with thanks to Dr Jurgen A Doornik) 190 191 **********************************************************************/ 192 193 double fraction () 194 { 195 return ((cast(int) natural()) * M_RAN_INVM32 + (0.5 + M_RAN_INVM32 / 2)); 196 } 197 198 /********************************************************************** 199 200 Returns a value in the range [0, 1) using 52 bits 201 of precision (with thanks to Dr Jurgen A Doornik) 202 203 **********************************************************************/ 204 205 double fractionEx () 206 { 207 return ((cast(int) natural()) * M_RAN_INVM32 + (0.5 + M_RAN_INVM52 / 2) + 208 ((cast(int) natural()) & 0x000FFFFF) * M_RAN_INVM52); 209 } 210 } 211 212 213 214 /****************************************************************************** 215 216 217 ******************************************************************************/ 218 219 debug (Kiss) 220 { 221 import tango.io.Stdout; 222 import tango.time.StopWatch; 223 224 void main() 225 { 226 auto dbl = Kiss(); 227 auto count = 100_000_000; 228 StopWatch w; 229 230 w.start; 231 double v1; 232 for (int i=count; --i;) 233 v1 = dbl.fraction; 234 Stdout.formatln ("{} fraction, {}/s, {:f10}", count, count/w.stop, v1); 235 236 w.start; 237 for (int i=count; --i;) 238 v1 = dbl.fractionEx; 239 Stdout.formatln ("{} fractionEx, {}/s, {:f10}", count, count/w.stop, v1); 240 241 for (int i=count; --i;) 242 { 243 auto v = dbl.fraction; 244 if (v <= 0.0 || v >= 1.0) 245 { 246 Stdout.formatln ("fraction {:f10}", v); 247 break; 248 } 249 v = dbl.fractionEx; 250 if (v <= 0.0 || v >= 1.0) 251 { 252 Stdout.formatln ("fractionEx {:f10}", v); 253 break; 254 } 255 } 256 } 257 }