| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258 | /******************************************************************************* copyright: Copyright (c) 2008. All rights reserved license: BSD style: $(LICENSE) version: Initial release: May 2008 author: Various Since: 0.99.7 With gratitude to Dr Jurgen A Doornik. See his paper entitled "Conversion of high-period random numbers to floating point" *******************************************************************************/ module tango.math.random.Kiss; version (Win32) private extern(Windows) int QueryPerformanceCounter (ulong *); version (Posix) { private import tango.stdc.posix.sys.time; } /****************************************************************************** KISS (from George Marsaglia) The idea is to use simple, fast, individually promising generators to get a composite that will be fast, easy to code have a very long period and pass all the tests put to it. The three components of KISS are --- x(n)=a*x(n-1)+1 mod 2^32 y(n)=y(n-1)(I+L^13)(I+R^17)(I+L^5), z(n)=2*z(n-1)+z(n-2) +carry mod 2^32 --- The y's are a shift register sequence on 32bit binary vectors period 2^32-1; The z's are a simple multiply-with-carry sequence with period 2^63+2^32-1. The period of KISS is thus --- 2^32*(2^32-1)*(2^63+2^32-1) > 2^127 --- Note that this should be passed by reference, unless you really intend to provide a local copy to a callee ******************************************************************************/ struct Kiss { /// public alias natural toInt; /// public alias fraction toReal; private uint kiss_k; private uint kiss_m; private uint kiss_x = 1; private uint kiss_y = 2; private uint kiss_z = 4; private uint kiss_w = 8; private uint kiss_carry = 0; private enum double M_RAN_INVM32 = 2.32830643653869628906e-010, M_RAN_INVM52 = 2.22044604925031308085e-016; /********************************************************************** A global, shared instance, seeded via startup time **********************************************************************/ public static __gshared Kiss instance; shared static this () { instance.seed(); } /********************************************************************** Creates and seeds a new generator with the current time **********************************************************************/ static Kiss opCall () { Kiss rand; rand.seed(); return rand; } /********************************************************************** Seed the generator with current time **********************************************************************/ void seed () { ulong s; version (Posix) { timeval tv; gettimeofday (&tv, null); s = tv.tv_usec; } version (Win32) QueryPerformanceCounter (&s); return seed (cast(uint) s); } /********************************************************************** Seed the generator with a provided value **********************************************************************/ void seed (uint seed) { kiss_x = seed | 1; kiss_y = seed | 2; kiss_z = seed | 4; kiss_w = seed | 8; kiss_carry = 0; } /********************************************************************** Returns X such that 0 <= X <= uint.max **********************************************************************/ uint natural () { kiss_x = kiss_x * 69069 + 1; kiss_y ^= kiss_y << 13; kiss_y ^= kiss_y >> 17; kiss_y ^= kiss_y << 5; kiss_k = (kiss_z >> 2) + (kiss_w >> 3) + (kiss_carry >> 2); kiss_m = kiss_w + kiss_w + kiss_z + kiss_carry; kiss_z = kiss_w; kiss_w = kiss_m; kiss_carry = kiss_k >> 30; return kiss_x + kiss_y + kiss_w; } /********************************************************************** Returns X such that 0 <= X < max Note that max is exclusive, making it compatible with array indexing **********************************************************************/ uint natural (uint max) { return natural() % max; } /********************************************************************** Returns X such that min <= X < max Note that max is exclusive, making it compatible with array indexing **********************************************************************/ uint natural (uint min, uint max) { return (natural() % (max-min)) + min; } /********************************************************************** Returns a value in the range [0, 1) using 32 bits of precision (with thanks to Dr Jurgen A Doornik) **********************************************************************/ double fraction () { return ((cast(int) natural()) * M_RAN_INVM32 + (0.5 + M_RAN_INVM32 / 2)); } /********************************************************************** Returns a value in the range [0, 1) using 52 bits of precision (with thanks to Dr Jurgen A Doornik) **********************************************************************/ double fractionEx () { return ((cast(int) natural()) * M_RAN_INVM32 + (0.5 + M_RAN_INVM52 / 2) + ((cast(int) natural()) & 0x000FFFFF) * M_RAN_INVM52); } } /****************************************************************************** ******************************************************************************/ debug (Kiss) { import tango.io.Stdout; import tango.time.StopWatch; void main() { auto dbl = Kiss(); auto count = 100_000_000; StopWatch w; w.start; double v1; for (int i=count; --i;) v1 = dbl.fraction; Stdout.formatln ("{} fraction, {}/s, {:f10}", count, count/w.stop, v1); w.start; for (int i=count; --i;) v1 = dbl.fractionEx; Stdout.formatln ("{} fractionEx, {}/s, {:f10}", count, count/w.stop, v1); for (int i=count; --i;) { auto v = dbl.fraction; if (v <= 0.0 || v >= 1.0) { Stdout.formatln ("fraction {:f10}", v); break; } v = dbl.fractionEx; if (v <= 0.0 || v >= 1.0) { Stdout.formatln ("fractionEx {:f10}", v); break; } } } } |