forked from perivar/FindSimilar
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSimpleRNG.cs
234 lines (206 loc) · 8.18 KB
/
SimpleRNG.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
using System;
namespace TestSimpleRNG
{
/// <summary>
/// SimpleRNG is a simple random number generator based on
/// George Marsaglia's MWC (multiply with carry) generator.
/// Although it is very simple, it passes Marsaglia's DIEHARD
/// series of random number generator tests.
///
/// Written by John D. Cook
/// http://www.johndcook.com
/// </summary>
public class SimpleRNG
{
private static uint m_w;
private static uint m_z;
static SimpleRNG()
{
// These values are not magical, just the default values Marsaglia used.
// Any pair of unsigned integers should be fine.
m_w = 521288629;
m_z = 362436069;
}
// The random generator seed can be set three ways:
// 1) specifying two non-zero unsigned integers
// 2) specifying one non-zero unsigned integer and taking a default value for the second
// 3) setting the seed from the system time
public static void SetSeed(uint u, uint v)
{
if (u != 0) m_w = u;
if (v != 0) m_z = v;
}
public static void SetSeed(uint u)
{
m_w = u;
}
public static void SetSeedFromSystemTime()
{
System.DateTime dt = System.DateTime.Now;
long x = dt.ToFileTime();
SetSeed((uint)(x >> 16), (uint)(x % 4294967296));
}
// Produce a uniform random sample from the open interval (0, 1).
// The method will not return either end point.
public static double GetUniform()
{
// 0 <= u < 2^32
uint u = GetUint();
// The magic number below is 1/(2^32 + 2).
// The result is strictly between 0 and 1.
return (u + 1.0) * 2.328306435454494e-10;
}
// This is the heart of the generator.
// It uses George Marsaglia's MWC algorithm to produce an unsigned integer.
// See http://www.bobwheeler.com/statistics/Password/MarsagliaPost.txt
private static uint GetUint()
{
m_z = 36969 * (m_z & 65535) + (m_z >> 16);
m_w = 18000 * (m_w & 65535) + (m_w >> 16);
return (m_z << 16) + m_w;
}
// Get normal (Gaussian) random sample with mean 0 and standard deviation 1
public static double GetNormal()
{
// Use Box-Muller algorithm
double u1 = GetUniform();
double u2 = GetUniform();
double r = Math.Sqrt( -2.0*Math.Log(u1) );
double theta = 2.0*Math.PI*u2;
return r*Math.Sin(theta);
}
// Get normal (Gaussian) random sample with specified mean and standard deviation
public static double GetNormal(double mean, double standardDeviation)
{
if (standardDeviation <= 0.0)
{
string msg = string.Format("Shape must be positive. Received {0}.", standardDeviation);
throw new ArgumentOutOfRangeException(msg);
}
return mean + standardDeviation*GetNormal();
}
// Get exponential random sample with mean 1
public static double GetExponential()
{
return -Math.Log( GetUniform() );
}
// Get exponential random sample with specified mean
public static double GetExponential(double mean)
{
if (mean <= 0.0)
{
string msg = string.Format("Mean must be positive. Received {0}.", mean);
throw new ArgumentOutOfRangeException(msg);
}
return mean*GetExponential();
}
public static double GetGamma(double shape, double scale)
{
// Implementation based on "A Simple Method for Generating Gamma Variables"
// by George Marsaglia and Wai Wan Tsang. ACM Transactions on Mathematical Software
// Vol 26, No 3, September 2000, pages 363-372.
double d, c, x, xsquared, v, u;
if (shape >= 1.0)
{
d = shape - 1.0/3.0;
c = 1.0/Math.Sqrt(9.0*d);
for (;;)
{
do
{
x = GetNormal();
v = 1.0 + c*x;
}
while (v <= 0.0);
v = v*v*v;
u = GetUniform();
xsquared = x*x;
if (u < 1.0 -.0331*xsquared*xsquared || Math.Log(u) < 0.5*xsquared + d*(1.0 - v + Math.Log(v)))
return scale*d*v;
}
}
else if (shape <= 0.0)
{
string msg = string.Format("Shape must be positive. Received {0}.", shape);
throw new ArgumentOutOfRangeException(msg);
}
else
{
double g = GetGamma(shape+1.0, 1.0);
double w = GetUniform();
return scale*g*Math.Pow(w, 1.0/shape);
}
}
public static double GetChiSquare(double degreesOfFreedom)
{
// A chi squared distribution with n degrees of freedom
// is a gamma distribution with shape n/2 and scale 2.
return GetGamma(0.5 * degreesOfFreedom, 2.0);
}
public static double GetInverseGamma(double shape, double scale)
{
// If X is gamma(shape, scale) then
// 1/Y is inverse gamma(shape, 1/scale)
return 1.0 / GetGamma(shape, 1.0 / scale);
}
public static double GetWeibull(double shape, double scale)
{
if (shape <= 0.0 || scale <= 0.0)
{
string msg = string.Format("Shape and scale parameters must be positive. Recieved shape {0} and scale{1}.", shape, scale);
throw new ArgumentOutOfRangeException(msg);
}
return scale * Math.Pow(-Math.Log(GetUniform()), 1.0 / shape);
}
public static double GetCauchy(double median, double scale)
{
if (scale <= 0)
{
string msg = string.Format("Scale must be positive. Received {0}.", scale);
throw new ArgumentException(msg);
}
double p = GetUniform();
// Apply inverse of the Cauchy distribution function to a uniform
return median + scale*Math.Tan(Math.PI*(p - 0.5));
}
public static double GetStudentT(double degreesOfFreedom)
{
if (degreesOfFreedom <= 0)
{
string msg = string.Format("Degrees of freedom must be positive. Received {0}.", degreesOfFreedom);
throw new ArgumentException(msg);
}
// See Seminumerical Algorithms by Knuth
double y1 = GetNormal();
double y2 = GetChiSquare(degreesOfFreedom);
return y1 / Math.Sqrt(y2 / degreesOfFreedom);
}
// The Laplace distribution is also known as the double exponential distribution.
public static double GetLaplace(double mean, double scale)
{
double u = GetUniform();
return (u < 0.5) ?
mean + scale*Math.Log(2.0*u) :
mean - scale*Math.Log(2*(1-u));
}
public static double GetLogNormal(double mu, double sigma)
{
return Math.Exp(GetNormal(mu, sigma));
}
public static double GetBeta(double a, double b)
{
if (a <= 0.0 || b <= 0.0)
{
string msg = string.Format("Beta parameters must be positive. Received {0} and {1}.", a, b);
throw new ArgumentOutOfRangeException(msg);
}
// There are more efficient methods for generating beta samples.
// However such methods are a little more efficient and much more complicated.
// For an explanation of why the following method works, see
// http://www.johndcook.com/distribution_chart.html#gamma_beta
double u = GetGamma(a, 1.0);
double v = GetGamma(b, 1.0);
return u / (u + v);
}
}
}