root/trunk/helix/random.d

Revision 12, 7.7 kB (checked in by nail, 6 years ago)

Added possibility to define left-opened interval for unit uniform engines

Line 
1 /*
2 Redistribution and use in source and binary forms, with or without
3 modification, are permitted provided that the following conditions
4 are met:
5
6     Redistributions of source code must retain the above copyright
7     notice, this list of conditions and the following disclaimer.
8
9     Redistributions in binary form must reproduce the above
10     copyright notice, this list of conditions and the following
11     disclaimer in the documentation and/or other materials provided
12     with the distribution.
13
14 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15 ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
17 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
18 REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
19 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
23 STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
25 OF THE POSSIBILITY OF SUCH DAMAGE.
26
27 Copyright (C) 2006. Victor Nakoryakov.
28 */
29 module helix.random;
30
31 private import helix.basic;
32
33 struct Rand48Engine
34 {
35     const static uint min = 0;
36     const static uint max = uint.max;
37
38     /**
39     Generates next pseudo-random number.
40     Returns:
41         Pseudo-random number in range [this.min; this.max]
42     */
43     uint pop()
44     {
45         x = (a * x + b) & mask;
46         return x >> 16;
47     }
48
49     /**
50     Reinitializes engine. Sets new _seed used for pseudo-random numbers generation.
51
52     If two different linear congruential engine would be initialized with the same
53     _seed they will generate equivalent pseudo-number sequences.
54     Params:
55         nx = New _seed used for pseudo-random numbers generation.
56     */
57     void seed(ulong nx)
58     {
59         x = nx & mask;
60     }
61    
62     private:
63         static const ulong a = 25214903917;
64         static const ulong b = 11;
65         static const ulong m = 1ul << 48;
66         static const ulong mask = m - 1;
67         ulong x = 0;
68 }
69
70 unittest
71 {
72     Rand48Engine e1;
73     e1.seed = 12345;
74     for (int i = 0; i < 100; ++i)
75         e1.pop();
76    
77     Rand48Engine e2 = e1;
78
79     // must generate the same sequence
80     for (int i = 0; i < 100; ++i)
81         assert(e1.pop() == e2.pop());
82
83     e1.seed = 54321;
84     e2.seed = 54321;
85
86     // must generate the same sequence
87     for (int i = 0; i < 100; ++i)
88         assert(e1.pop() == e2.pop());
89 }
90
91 /*********************************************************************/
92 struct MersenneTwisterEngine
93 {
94     static const uint min = 0;
95     static const uint max = uint.max;
96
97     static const uint n = 624;
98     static const uint m = 397;
99
100     uint pop()
101     {
102         if (next >= n) // overflow, engine reload needed
103         {
104             uint twist(uint m, uint u, uint v)
105             {
106                 return m ^ (((u & 0x8000_0000u) | (v & 0x7fff_ffffu)) >> 1) ^
107                     (-(u & 0x1u) & 0x9908_b0dfu);
108             }
109
110             uint* p = s.ptr;
111            
112             for (int i = n - m; i--; ++p)
113                 *p = twist( p[m], p[0], p[1] );
114
115             for (int i = m; --i; ++p)
116                 *p = twist( p[m - n], p[0], p[1] );
117
118             *p = twist( p[m - n], p[0], s[0] );
119
120             next = 0;
121         }
122
123         // use 'state.ptr[next]' instead of 'state[next]' to
124         // suppress array bound checks, namely performance penalty
125         uint x = s.ptr[next];
126         ++next;
127
128         x ^= (x >> 11);
129         x ^= (x <<  7) & 0x9d2c_5680u;
130         x ^= (x << 15) & 0xefc6_0000u;
131         return x ^ (x >> 18);
132     }
133
134     void seed(uint x)
135     {
136         s[0] = x;
137         for (int i = 1; i < n; ++i)
138             s[i] = 1_812_433_253u * (s[i-1] ^ (s[i-1] >> 30)) + i;
139
140         next = 1;
141     }
142
143     private:
144         uint[n] s = void;
145         uint next = 0;     
146 }
147
148 unittest
149 {
150     MersenneTwisterEngine e1;
151     e1.seed = 12345;
152     for (int i = 0; i < 100; ++i)
153         e1.pop();
154    
155     MersenneTwisterEngine e2 = e1;
156
157     // must generate the same sequence
158     for (int i = 0; i < 100; ++i)
159         assert(e1.pop() == e2.pop());
160
161     e1.seed = 54321;
162     e2.seed = 54321;
163
164     // must generate the same sequence
165     for (int i = 0; i < 100; ++i)
166         assert(e1.pop() == e2.pop());
167 }
168
169 /********************************************************************/
170 struct UnitUniformEngine(BaseEngine, bool closedLeft, bool closedRight)
171 {
172     private BaseEngine baseEngine;
173
174     const static
175     {
176         real min = (closedLeft ? 0 : increment) * (1/denominator);
177         real max = (range + (closedLeft ? 0 : increment)) * (1/denominator);
178     }
179
180     private const static
181     {
182         real range = cast(real)(baseEngine.max - baseEngine.min);
183         real increment = (baseEngine.max > uint.max) ? 2.l : 0.2l;
184         real denominator = range + (closedLeft ? 0 : increment) + (closedRight ? 0 : increment);
185     }
186
187     real pop()
188     {
189         auto x = baseEngine.pop();
190        
191         static if (
192             is (typeof(baseEngine.pop) : real) && // base engine pops float-type values
193             cast(real)baseEngine.min == this.min &&
194             cast(real)baseEngine.max == this.max)
195         {
196             // no manipulations required, return value as is.
197             return cast(real)x;
198         }
199         else
200         {
201             return (cast(real)(x - baseEngine.min) + (closedLeft ? 0 : increment)) * (1/denominator);
202         }
203     }
204
205     void seed(uint x)
206     {
207         baseEngine.seed = x;
208     }
209 }
210
211 unittest
212 {
213     alias UnitUniformEngine!(Rand48Engine, true, true) fullClosed;
214     alias UnitUniformEngine!(Rand48Engine, true, false) closedLeft;
215     alias UnitUniformEngine!(Rand48Engine, false, true) closedRight;
216     alias UnitUniformEngine!(Rand48Engine, false, false) fullOpened;
217
218     static assert(fullClosed.min == 0.l);
219     static assert(fullClosed.max == 1.l);
220
221     static assert(closedLeft.min == 0.l);
222     static assert(closedLeft.max < 1.l);
223    
224     static assert(closedRight.min > 0.l);
225     static assert(closedRight.max == 1.l);
226
227     static assert(fullOpened.min > 0.l);
228     static assert(fullOpened.max < 1.l);
229 }
230
231 /********************************************************************/
232 struct HighresUnitUniformEngine(BaseEngine, bool closedLeft, bool closedRight)
233 {
234     private BaseEngine baseEngine;
235
236     static const
237     {
238         real min = (closedLeft ? 0 : increment) * (1 / denominator);
239         real max = (rawMax + (closedLeft ? 0 : increment)) * (1 / denominator);
240     }
241
242     private const static
243     {
244         real rawMax = uint.max * 0x1p32 + uint.max;
245         real increment = 2.l;
246         real denominator = rawMax + (closedLeft ? 0 : increment) + (closedRight ? 0 : increment);
247     }
248
249     real pop()
250     {
251         static if (
252             is (typeof(baseEngine.pop) : real) && // base engine pops float-type values
253             cast(real)baseEngine.min == this.min &&
254             cast(real)baseEngine.max == this.max)
255         {
256             // no manipulations required, return value as is.
257             return cast(real)baseEngine.pop();
258         }
259         else
260         {
261             // this is necessary condition to generate truly uniform
262             // result. However it is possible to use base engine with any range,
263             // but this feature isn't implemented for now and can be introduced
264             // in future.
265             static assert( baseEngine.min == 0 && baseEngine.max == uint.max );
266
267             uint a = cast(uint)baseEngine.pop();
268             uint b = cast(uint)baseEngine.pop();
269             return (a * 0x1p32 + b + (closedLeft ? 0 : increment)) * (1 / denominator);
270         }
271     }
272
273     void seed(uint x)
274     {
275         baseEngine.seed = x;
276     }
277 }
278
279 unittest
280 {
281     alias HighresUnitUniformEngine!(Rand48Engine, true, true) fullClosed;
282     alias HighresUnitUniformEngine!(Rand48Engine, true, false) closedLeft;
283     alias HighresUnitUniformEngine!(Rand48Engine, false, true) closedRight;
284     alias HighresUnitUniformEngine!(Rand48Engine, false, false) fullOpened;
285
286     static assert(fullClosed.min == 0.l);
287     static assert(fullClosed.max == 1.l);
288
289     static assert(closedLeft.min == 0.l);
290     static assert(closedLeft.max < 1.l);
291    
292     static assert(closedRight.min > 0.l);
293     static assert(closedRight.max == 1.l);
294
295     static assert(fullOpened.min > 0.l);
296     static assert(fullOpened.max < 1.l);
297 }
Note: See TracBrowser for help on using the browser.