randomext 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/ext/binomial.c +323 -0
- data/ext/extconf.rb +3 -0
- data/ext/gamma.c +32 -0
- data/ext/hypergeometric.c +81 -0
- data/ext/other.c +122 -0
- data/ext/poisson.c +65 -0
- data/ext/randomext.h +27 -0
- data/ext/randomext_native.c +76 -0
- data/ext/standard_exponential.c +79 -0
- data/ext/standard_normal.c +94 -0
- data/lib/randomext.rb +373 -0
- metadata +60 -0
data/ext/binomial.c
ADDED
@@ -0,0 +1,323 @@
|
|
1
|
+
#include "randomext.h"
|
2
|
+
|
3
|
+
typedef struct {
|
4
|
+
int n;
|
5
|
+
double theta;
|
6
|
+
int N;
|
7
|
+
int k;
|
8
|
+
double *p;
|
9
|
+
int *T;
|
10
|
+
int *K;
|
11
|
+
double *V;
|
12
|
+
} binomial_t;
|
13
|
+
|
14
|
+
static double binomial_distribution(int k, int n, double theta)
|
15
|
+
{
|
16
|
+
double ret = 1.0;
|
17
|
+
int i;
|
18
|
+
for (i=1; i <= k; ++i) {
|
19
|
+
ret *= n-k+i;
|
20
|
+
ret /= i;
|
21
|
+
ret *= theta;
|
22
|
+
if (i <= n - k)
|
23
|
+
ret *= 1 - theta;
|
24
|
+
}
|
25
|
+
for (i=0; i<n-2*k; ++i)
|
26
|
+
ret *= 1 - theta;
|
27
|
+
|
28
|
+
return ret;
|
29
|
+
}
|
30
|
+
|
31
|
+
/* Returns Bin(x+1| n, theta)/Bin(x| n, theta) */
|
32
|
+
inline static double forward_ratio(double x, double n, double theta)
|
33
|
+
{
|
34
|
+
return ((n+1)/(x+1) - 1)*(theta/(1-theta));
|
35
|
+
}
|
36
|
+
|
37
|
+
/* Returns Bin(x-1| n, theta)/Bin(x| n, theta) */
|
38
|
+
inline static double backward_ratio(double x, double n, double theta)
|
39
|
+
{
|
40
|
+
return ((n+1)/(n+1-x)-1)*((1-theta)/theta);
|
41
|
+
}
|
42
|
+
|
43
|
+
static void check_binomial_params(int n, double theta, const char* method_name)
|
44
|
+
{
|
45
|
+
if (n < 1)
|
46
|
+
rb_raise(rb_eArgError, "%s: n must be >= 1", method_name);
|
47
|
+
if (theta <= 0.0 || 1.0 <= theta)
|
48
|
+
rb_raise(rb_eArgError, "%s: n must be in (0, 1)", method_name);
|
49
|
+
|
50
|
+
}
|
51
|
+
/*
|
52
|
+
* Draws a random sample from a binomial distribution
|
53
|
+
*
|
54
|
+
* Inverse function method is used.
|
55
|
+
*
|
56
|
+
* @overload binomial(n, theta)
|
57
|
+
* @param [Integer] n the number of trials (n > 0)
|
58
|
+
* @param [Float] theta success probability (0 < theta < 1)
|
59
|
+
* @return [Integer] a random sample in 0..n
|
60
|
+
*/
|
61
|
+
static VALUE random_binomial_inv(VALUE self, VALUE num, VALUE prob)
|
62
|
+
{
|
63
|
+
int n = NUM2INT(num);
|
64
|
+
double theta = NUM2DBL(prob);
|
65
|
+
int mode = floor(theta*(n+1));
|
66
|
+
int xl = mode;
|
67
|
+
int xu = mode+1;
|
68
|
+
double pl = binomial_distribution(xl, n, theta);
|
69
|
+
double pu = pl*forward_ratio(xl, n, theta);
|
70
|
+
double u = rb_random_real(self);
|
71
|
+
|
72
|
+
check_binomial_params(n, theta, "Random#binomial");
|
73
|
+
|
74
|
+
for (;xl >=0 || xu <= n;) {
|
75
|
+
if (xl >= 0) {
|
76
|
+
if (u <= pl)
|
77
|
+
return INT2NUM(xl);
|
78
|
+
u = u - pl;
|
79
|
+
pl *= backward_ratio(xl, n, theta);
|
80
|
+
--xl;
|
81
|
+
}
|
82
|
+
if (xu <= n) {
|
83
|
+
if (u <= pu)
|
84
|
+
return INT2NUM(xu);
|
85
|
+
u = u - pu;
|
86
|
+
pu *= forward_ratio(xu, n, theta);
|
87
|
+
++xu;
|
88
|
+
}
|
89
|
+
}
|
90
|
+
|
91
|
+
return INT2FIX(0);
|
92
|
+
}
|
93
|
+
|
94
|
+
static void fill_binomial_table(binomial_t *bin)
|
95
|
+
{
|
96
|
+
double theta = bin->theta;
|
97
|
+
double n = bin->n;
|
98
|
+
int mode = floor(theta*(n+1));
|
99
|
+
int k;
|
100
|
+
|
101
|
+
bin->p[mode] = binomial_distribution(mode, n, theta);
|
102
|
+
for (k = mode+1; k <= n; ++k) {
|
103
|
+
bin->p[k] = bin->p[k-1]*forward_ratio(k-1, n, theta);
|
104
|
+
}
|
105
|
+
for (k = mode-1; k >= 0; --k) {
|
106
|
+
bin->p[k] = bin->p[k+1]*backward_ratio(k+1, n, theta);
|
107
|
+
}
|
108
|
+
}
|
109
|
+
|
110
|
+
static void fill_binomial_VK_table(binomial_t *bin, double ntheta[])
|
111
|
+
{
|
112
|
+
double c = 1.0/(bin->n + 1);
|
113
|
+
int i, j, n, x;
|
114
|
+
|
115
|
+
bin->K = ALLOC_N(int, bin->n + 1);
|
116
|
+
bin->V = ALLOC_N(double, bin->n + 1);
|
117
|
+
|
118
|
+
for (i=0; i<=bin->n; ++i) {
|
119
|
+
bin->K[i] = i;
|
120
|
+
bin->V[i] = (i+1)*c;
|
121
|
+
}
|
122
|
+
|
123
|
+
for (n=0; n<bin->n; ++n) {
|
124
|
+
for (x=0, i=j=0; x<=bin->n; ++x) {
|
125
|
+
if (ntheta[x] < ntheta[i])
|
126
|
+
i = x;
|
127
|
+
if (ntheta[x] > ntheta[j])
|
128
|
+
j = x;
|
129
|
+
}
|
130
|
+
bin->K[i] = j;
|
131
|
+
bin->V[i] = i*c + ntheta[i];
|
132
|
+
ntheta[j] = ntheta[j] - (c - ntheta[i]);
|
133
|
+
ntheta[i] = c;
|
134
|
+
}
|
135
|
+
}
|
136
|
+
|
137
|
+
static void fill_binomial_T_VT_table(binomial_t *bin)
|
138
|
+
{
|
139
|
+
int k, i, x;
|
140
|
+
int nt;
|
141
|
+
int *qt = ALLOC_N(int, bin->n + 2);
|
142
|
+
double *theta = ALLOC_N(double, bin->n + 1);
|
143
|
+
double *ntheta = ALLOC_N(double, bin->n + 1);
|
144
|
+
double sum_theta = 0;
|
145
|
+
|
146
|
+
for (k=7; ;k++) {
|
147
|
+
int b = pow2(k);
|
148
|
+
nt = 0;
|
149
|
+
qt[0] = 0;
|
150
|
+
for (x=0; x<=bin->n; x++) {
|
151
|
+
qt[x+1] = floor(b*bin->p[x]) + qt[x];
|
152
|
+
}
|
153
|
+
if (k > 16 || qt[bin->n + 1] > 0.9*b)
|
154
|
+
break;
|
155
|
+
}
|
156
|
+
|
157
|
+
bin->k = k;
|
158
|
+
bin->N = pow2(k);
|
159
|
+
bin->T = ALLOC_N(int, bin->N);
|
160
|
+
for (x=0; x<=bin->n; ++x) {
|
161
|
+
for (i=qt[x]; i<qt[x+1]; ++i)
|
162
|
+
bin->T[i] = x;
|
163
|
+
}
|
164
|
+
for (i=qt[bin->n + 1]; i<bin->N; ++i)
|
165
|
+
bin->T[i] = -1;
|
166
|
+
|
167
|
+
for (x=0; x<=bin->n; ++x) {
|
168
|
+
theta[x] = pow2(k) * bin->p[x] - (qt[x+1]-qt[x]);
|
169
|
+
sum_theta += theta[x];
|
170
|
+
}
|
171
|
+
|
172
|
+
for (x=0; x<=bin->n; ++x)
|
173
|
+
ntheta[x] = theta[x]/sum_theta;
|
174
|
+
|
175
|
+
fill_binomial_VK_table(bin, ntheta);
|
176
|
+
|
177
|
+
xfree(qt);
|
178
|
+
xfree(theta);
|
179
|
+
xfree(ntheta);
|
180
|
+
}
|
181
|
+
|
182
|
+
static void binomial_free(binomial_t *bin)
|
183
|
+
{
|
184
|
+
xfree(bin->p);
|
185
|
+
xfree(bin->T);
|
186
|
+
xfree(bin->K);
|
187
|
+
xfree(bin->V);
|
188
|
+
xfree(bin);
|
189
|
+
}
|
190
|
+
|
191
|
+
static VALUE binomial_alloc(VALUE klass)
|
192
|
+
{
|
193
|
+
binomial_t *bin;
|
194
|
+
VALUE obj = Data_Make_Struct(klass, binomial_t, 0, binomial_free, bin);
|
195
|
+
bin->n = -1;
|
196
|
+
bin->p = NULL; bin->T = NULL; bin->K = NULL; bin->V = NULL;
|
197
|
+
return obj;
|
198
|
+
}
|
199
|
+
|
200
|
+
/*
|
201
|
+
* Returns a random sampler from a binomial distribution.
|
202
|
+
*
|
203
|
+
* This sampler uses table plus square histgram method with
|
204
|
+
* Robin Hoot method. This method constructs a table for each
|
205
|
+
* distribution. If you once construct the table, you can
|
206
|
+
* draw a random sample fast for large n (n >= 40), but the
|
207
|
+
* cost of the table construction is expensive. Therefore,
|
208
|
+
* if you need to draw many samples from the same binomial distribution,
|
209
|
+
* you had better to use this class. Otherwise, you should use
|
210
|
+
* Random#binomial.
|
211
|
+
*
|
212
|
+
* @overload initialize(rng, n, theta)
|
213
|
+
* @param [Random] rng a random number generator
|
214
|
+
* @param [Integer] n the number of trials (n > 0)
|
215
|
+
* @param [Float] theta success probability (0 < theta < 1)
|
216
|
+
* @return [Random::Binomial] a random number generator from the specified binomial distribution
|
217
|
+
*/
|
218
|
+
static VALUE binomial_initialize(VALUE self, VALUE rng, VALUE num, VALUE prob)
|
219
|
+
{
|
220
|
+
binomial_t *bin;
|
221
|
+
int n = NUM2INT(num);
|
222
|
+
double theta = NUM2DBL(prob);
|
223
|
+
|
224
|
+
check_binomial_params(n, theta, "Random::Binomial.new");
|
225
|
+
|
226
|
+
rb_iv_set(self, "rng", rng);
|
227
|
+
Data_Get_Struct(self, binomial_t, bin);
|
228
|
+
bin->n = n;
|
229
|
+
bin->theta = theta;
|
230
|
+
bin->p = ALLOC_N(double, bin->n + 1);
|
231
|
+
|
232
|
+
fill_binomial_table(bin);
|
233
|
+
fill_binomial_T_VT_table(bin);
|
234
|
+
|
235
|
+
return Qnil;
|
236
|
+
}
|
237
|
+
|
238
|
+
/*
|
239
|
+
* Draws a sample from the binomimial distribution whose parameters
|
240
|
+
* are specified in Random::Binomial.new.
|
241
|
+
*
|
242
|
+
* @return [Integer] a random sample
|
243
|
+
*/
|
244
|
+
static VALUE binomial_rand(VALUE self)
|
245
|
+
{
|
246
|
+
VALUE rng = rb_iv_get(self, "rng");
|
247
|
+
binomial_t *bin;
|
248
|
+
uint32_t I0 = rb_random_int32(rng);
|
249
|
+
uint32_t ILk;
|
250
|
+
double U;
|
251
|
+
int J;
|
252
|
+
|
253
|
+
Data_Get_Struct(self, binomial_t, bin);
|
254
|
+
|
255
|
+
ILk = I0 & MASK(bin->k);
|
256
|
+
if (bin->T[ILk] >= 0)
|
257
|
+
return INT2NUM(bin->T[ILk]);
|
258
|
+
|
259
|
+
U = rb_random_real(rng);
|
260
|
+
J = floor((bin->n + 1)*U);
|
261
|
+
if (U < bin->V[J])
|
262
|
+
return INT2NUM(J);
|
263
|
+
else
|
264
|
+
return INT2NUM(bin->K[J]);
|
265
|
+
}
|
266
|
+
|
267
|
+
/*
|
268
|
+
* @!attribute [r] n
|
269
|
+
* @return [Integer] the parameter n
|
270
|
+
*/
|
271
|
+
static VALUE binomial_n(VALUE self)
|
272
|
+
{
|
273
|
+
binomial_t *bin;
|
274
|
+
Data_Get_Struct(self, binomial_t, bin);
|
275
|
+
|
276
|
+
return INT2NUM(bin->n);
|
277
|
+
}
|
278
|
+
|
279
|
+
/*
|
280
|
+
* @!attribute [r] theta
|
281
|
+
* @return [Float] the parameter theta
|
282
|
+
*/
|
283
|
+
static VALUE binomial_theta(VALUE self)
|
284
|
+
{
|
285
|
+
binomial_t *bin;
|
286
|
+
Data_Get_Struct(self, binomial_t, bin);
|
287
|
+
|
288
|
+
return DBL2NUM(bin->theta);
|
289
|
+
}
|
290
|
+
|
291
|
+
#if 0
|
292
|
+
static VALUE binomial_debug_info(VALUE self)
|
293
|
+
{
|
294
|
+
binomial_t *bin;
|
295
|
+
int i;
|
296
|
+
|
297
|
+
Data_Get_Struct(self, binomial_t, bin);
|
298
|
+
|
299
|
+
|
300
|
+
printf("N=%d\n", bin->N);
|
301
|
+
for (i=0; i<bin->N; ++i) {
|
302
|
+
printf("%d ", bin->T[i]);
|
303
|
+
}
|
304
|
+
puts("");
|
305
|
+
for (i=0; i<=bin->n; ++i) {
|
306
|
+
printf("%f %d\n", bin->V[i], bin->K[i]);
|
307
|
+
}
|
308
|
+
return Qnil;
|
309
|
+
}
|
310
|
+
#endif
|
311
|
+
|
312
|
+
void randomext_binomial_init(VALUE cRandom)
|
313
|
+
{
|
314
|
+
VALUE cBinomial = rb_define_class_under(cRandom, "Binomial", rb_cObject);
|
315
|
+
|
316
|
+
rb_define_method(cRandom, "binomial", random_binomial_inv, 2);
|
317
|
+
rb_define_alloc_func(cBinomial, binomial_alloc);
|
318
|
+
rb_define_method(cBinomial, "initialize", binomial_initialize, 3);
|
319
|
+
rb_define_method(cBinomial, "rand", binomial_rand, 0);
|
320
|
+
rb_define_method(cBinomial, "n", binomial_n, 0);
|
321
|
+
rb_define_method(cBinomial, "theta", binomial_theta, 0);
|
322
|
+
//rb_define_method(cBinomial, "debug_info", binomial_debug_info, 0);
|
323
|
+
}
|
data/ext/extconf.rb
ADDED
data/ext/gamma.c
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
#include "randomext.h"
|
2
|
+
|
3
|
+
/*
|
4
|
+
* @private
|
5
|
+
*/
|
6
|
+
static VALUE random_gamma(VALUE self, VALUE shape)
|
7
|
+
{
|
8
|
+
double c, d;
|
9
|
+
|
10
|
+
if (NUM2DBL(shape) < 1.0)
|
11
|
+
rb_raise(rb_eArgError, "Random#_gamma: shape parameter must be >= 1.0");
|
12
|
+
|
13
|
+
d = NUM2DBL(shape) - 1.0/3.0;
|
14
|
+
c = 1/sqrt(9*d);
|
15
|
+
|
16
|
+
for (;;) {
|
17
|
+
double z, v, y, w, u;
|
18
|
+
z = randomext_random_standard_normal(self);
|
19
|
+
v = 1 + c*z;
|
20
|
+
if (v <= 0) continue;
|
21
|
+
w = v*v*v; y = d*w;
|
22
|
+
u = random_open_interval(self);
|
23
|
+
if (u > 1 - 0.0331*(z*z*z*z) && z*z/2 + d*log(w) - y + d < log(u))
|
24
|
+
continue;
|
25
|
+
return DBL2NUM(y);
|
26
|
+
}
|
27
|
+
}
|
28
|
+
|
29
|
+
void randomext_gamma_init(VALUE cRandom)
|
30
|
+
{
|
31
|
+
rb_define_private_method(cRandom, "_gamma", random_gamma, 1);
|
32
|
+
}
|
@@ -0,0 +1,81 @@
|
|
1
|
+
#include "randomext.h"
|
2
|
+
|
3
|
+
/* Returns HGeo(x+1| N, M, n)/HGeo(x| N, M, n) */
|
4
|
+
inline static double forward_ratio(int x, int N, int M, int n)
|
5
|
+
{
|
6
|
+
return (double)((M-x)*(n-x))/((x+1)*(N-M-n+x+1));
|
7
|
+
}
|
8
|
+
|
9
|
+
/* Returns HGeo(x-1| N, M, n)/HGeo(x| N, M, n) */
|
10
|
+
inline static double backward_ratio(int x, int N, int M, int n)
|
11
|
+
{
|
12
|
+
return 1.0/forward_ratio(x-1, N, M, n);
|
13
|
+
}
|
14
|
+
|
15
|
+
/* Returns HGeo(x| N, M, n) */
|
16
|
+
static inline double hypergeometric_distribution(int x, int N, int M, int n)
|
17
|
+
{
|
18
|
+
return exp(randomext_logcombination(M, x)
|
19
|
+
+ randomext_logcombination(N-M, n-x)
|
20
|
+
- randomext_logcombination(N, n));
|
21
|
+
}
|
22
|
+
|
23
|
+
/*
|
24
|
+
* Draws a random sample from a hypergeometric distribution.
|
25
|
+
*
|
26
|
+
* Inverse method is used.
|
27
|
+
*
|
28
|
+
* @overload hypergeometric(N, M, n)
|
29
|
+
* @param [Integer] N a population (N >= 0)
|
30
|
+
* @param [Integer] M the number of successes (0 <= M <= N)
|
31
|
+
* @param [Integer] n the number of samples (0 <= n <= N)
|
32
|
+
* @return [Integer] a random sample in [max(0, n-(N-M)), min(n, M)]
|
33
|
+
*/
|
34
|
+
static VALUE random_hypergoemtric_inv(VALUE self, VALUE vN, VALUE vM, VALUE vn)
|
35
|
+
{
|
36
|
+
int N = NUM2INT(vN);
|
37
|
+
int M = NUM2INT(vM);
|
38
|
+
int n = NUM2INT(vn);
|
39
|
+
int ok = (N >= 0) && (M >= 0) && (n >= 0) && (M <= N) && (n <= N);
|
40
|
+
int mode, x_min, x_max, xu, xl;
|
41
|
+
double pl, pu;
|
42
|
+
double u;
|
43
|
+
|
44
|
+
if (!ok)
|
45
|
+
rb_raise(rb_eArgError,
|
46
|
+
"Random#hypergeometric: paramters must be:"
|
47
|
+
"(N >= 0) && (M >= 0) && (n >= 0) && (M <= N) && (n <= N)");
|
48
|
+
|
49
|
+
mode = (M+1)*(n+1) / (N+2);
|
50
|
+
x_min = MAX2(0, n - (N-M));
|
51
|
+
x_max = MIN2(n, M);
|
52
|
+
xu = mode;
|
53
|
+
pu = hypergeometric_distribution(mode, N, M, n);
|
54
|
+
xl = mode-1;
|
55
|
+
pl = pu * backward_ratio(mode, N, M, n);
|
56
|
+
u = rb_random_real(self);
|
57
|
+
|
58
|
+
for (;x_min <= xl || xu <= x_max;) {
|
59
|
+
if (xu <= x_max) {
|
60
|
+
if (u <= pu)
|
61
|
+
return INT2NUM(xu);
|
62
|
+
u -= pu;
|
63
|
+
pu *= forward_ratio(xu, N, M, n);
|
64
|
+
++xu;
|
65
|
+
}
|
66
|
+
if (xl >= x_min) {
|
67
|
+
if (u <= pl)
|
68
|
+
return INT2NUM(xl);
|
69
|
+
u -= pl;
|
70
|
+
pl *= backward_ratio(xl, N, M, n);
|
71
|
+
--xl;
|
72
|
+
}
|
73
|
+
}
|
74
|
+
|
75
|
+
return INT2NUM(x_min);
|
76
|
+
}
|
77
|
+
|
78
|
+
void randomext_hypergeometric_init(VALUE cRandom)
|
79
|
+
{
|
80
|
+
rb_define_method(cRandom, "hypergeometric", random_hypergoemtric_inv, 3);
|
81
|
+
}
|
data/ext/other.c
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
#include "randomext.h"
|
2
|
+
|
3
|
+
/*
|
4
|
+
* Draws a random sample from a von Mises distribution.
|
5
|
+
*
|
6
|
+
* The return value is contained in [-PI, PI].
|
7
|
+
*
|
8
|
+
* @overload vonmises(mu, kappa)
|
9
|
+
* @param [Float] mu direction parameter (-PI <= mu <= PI)
|
10
|
+
* @param [Float] kappa concentration parameter (kappa > 0)
|
11
|
+
* @return [Float] A random sample in [-PI, PI]
|
12
|
+
*/
|
13
|
+
static VALUE random_vonmises(VALUE self, VALUE vmu, VALUE vkappa)
|
14
|
+
{
|
15
|
+
double mu = NUM2DBL(vmu);
|
16
|
+
double kappa = NUM2DBL(vkappa);
|
17
|
+
double s;
|
18
|
+
|
19
|
+
if (kappa <= 0)
|
20
|
+
rb_raise(rb_eArgError, "Random#vonmises: parameter kappa must be positive");
|
21
|
+
|
22
|
+
s = (1 + sqrt(1+4*kappa*kappa))/(2*kappa);
|
23
|
+
|
24
|
+
for (;;) {
|
25
|
+
double u = rb_random_real(self);
|
26
|
+
double z = cos(2*M_PI*u);
|
27
|
+
double W = (1-s*z)/(s-z);
|
28
|
+
double T = kappa*(s-W);
|
29
|
+
double U = rb_random_real(self);
|
30
|
+
double V = rb_random_real(self);
|
31
|
+
double x, y;
|
32
|
+
|
33
|
+
if (V > T*(2-T) && V > T*exp(1-T))
|
34
|
+
continue;
|
35
|
+
|
36
|
+
if (U < 0.5)
|
37
|
+
y = -acos(W);
|
38
|
+
else
|
39
|
+
y = acos(W);
|
40
|
+
x = y + mu;
|
41
|
+
if (x >= M_PI)
|
42
|
+
return DBL2NUM(x - M_PI);
|
43
|
+
else if (x < -M_PI)
|
44
|
+
return DBL2NUM(x + M_PI);
|
45
|
+
else
|
46
|
+
return DBL2NUM(x);
|
47
|
+
}
|
48
|
+
}
|
49
|
+
|
50
|
+
/*
|
51
|
+
* Draws a random sample from a Zipf-Mandelbrot distribution.
|
52
|
+
*
|
53
|
+
* In case of q == 0.0, the distribution is called a Zipf distribution.
|
54
|
+
*
|
55
|
+
* @overload zipf_mandelbrot(n, q=0.0, s=1.0)
|
56
|
+
* @param [Integer] n the maximum of return value (n > 0)
|
57
|
+
* @param [Float] q a parameter (q >= 0.0)
|
58
|
+
* @param [Float] s a parameter (s > 0.0)
|
59
|
+
* @return [Integer] a random sample in 1..n
|
60
|
+
*/
|
61
|
+
static VALUE random_zipf(int argc, VALUE *argv, VALUE self)
|
62
|
+
{
|
63
|
+
VALUE vN, vs, vq;
|
64
|
+
int N;
|
65
|
+
double s, q;
|
66
|
+
double sum;
|
67
|
+
int i;
|
68
|
+
double u;
|
69
|
+
|
70
|
+
rb_scan_args(argc, argv, "12", &vN, &vq, &vs);
|
71
|
+
N = NUM2INT(vN);
|
72
|
+
s = NIL_P(vs) ? 1.0 : NUM2DBL(vs);
|
73
|
+
q = NIL_P(vq) ? 0.0 : NUM2DBL(vq);
|
74
|
+
|
75
|
+
if (N <= 0 || s <= 0 || q < 0)
|
76
|
+
rb_raise(rb_eArgError, "Random#zipf_mandelbrot: parameters must be N >0, s > 0, q >= 0");
|
77
|
+
|
78
|
+
for (i=1, sum=0; i<=N; ++i)
|
79
|
+
sum += 1.0/pow(i+q, s);
|
80
|
+
|
81
|
+
u = rb_random_real(self);
|
82
|
+
|
83
|
+
for (i=1; i<=N; ++i) {
|
84
|
+
double p = 1.0/pow(i+q, s)/sum;
|
85
|
+
if (u <= p)
|
86
|
+
return INT2NUM(i);
|
87
|
+
u -= p;
|
88
|
+
}
|
89
|
+
|
90
|
+
return INT2NUM(N);
|
91
|
+
}
|
92
|
+
|
93
|
+
/*
|
94
|
+
* Draws a random sample from a zeta distribution.
|
95
|
+
*
|
96
|
+
* @overload zeta(s)
|
97
|
+
* @param [Integer] s a parameter (s > 0.0)
|
98
|
+
* @return [Integer] a random sample in [1, INFINITY)
|
99
|
+
*/
|
100
|
+
static VALUE random_zeta(VALUE self, VALUE vs)
|
101
|
+
{
|
102
|
+
double s = NUM2DBL(vs);
|
103
|
+
double q = s - 1.0;
|
104
|
+
double r = -1.0/q;
|
105
|
+
double t = pow(2.0, q);
|
106
|
+
|
107
|
+
for (;;) {
|
108
|
+
double u = 1.0 - rb_random_real(self);
|
109
|
+
double v = rb_random_real(self);
|
110
|
+
int x = floor(pow(u, r));
|
111
|
+
double w = pow(1.0 + 1.0/x, q);
|
112
|
+
if (v*x*(w-1)/(t-1) <= w/t)
|
113
|
+
return INT2NUM(x);
|
114
|
+
}
|
115
|
+
}
|
116
|
+
|
117
|
+
void randomext_other_init(VALUE cRandom)
|
118
|
+
{
|
119
|
+
rb_define_method(cRandom, "vonmises", random_vonmises, 2);
|
120
|
+
rb_define_method(cRandom, "zipf_mandelbrot", random_zipf, -1);
|
121
|
+
rb_define_method(cRandom, "zeta", random_zeta, 1);
|
122
|
+
}
|
data/ext/poisson.c
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
#include "randomext.h"
|
2
|
+
|
3
|
+
static double poisson_distribution(int m, double lambda)
|
4
|
+
{
|
5
|
+
return exp(-lambda + m*log(lambda) - randomext_sumlog(1, m));
|
6
|
+
}
|
7
|
+
|
8
|
+
/* Returns Poi(x+1|lambda)/Poi(x|lambda) */
|
9
|
+
static inline double forward_ratio(int x, double lambda)
|
10
|
+
{
|
11
|
+
return lambda/(x+1);
|
12
|
+
}
|
13
|
+
|
14
|
+
/* Returns Poi(x-1|lambda)/Poi(x|lambda) */
|
15
|
+
static inline double backward_ratio(int x, double lambda)
|
16
|
+
{
|
17
|
+
return x/lambda;
|
18
|
+
}
|
19
|
+
|
20
|
+
/*
|
21
|
+
* Draws a random sample from a Poisson distribution.
|
22
|
+
*
|
23
|
+
* Inverse function method is used.
|
24
|
+
*
|
25
|
+
* @overload poisson(lambda)
|
26
|
+
* @param [Float] lambda mean
|
27
|
+
* @return [Integer] a random sample in [0, INFINITY)
|
28
|
+
*/
|
29
|
+
static VALUE random_poisson_inv(VALUE self, VALUE l)
|
30
|
+
{
|
31
|
+
double lambda = NUM2DBL(l);
|
32
|
+
int mode, xu, xl;
|
33
|
+
double pu, pl, u;
|
34
|
+
|
35
|
+
if (lambda <= 0.0)
|
36
|
+
rb_raise(rb_eArgError, "Random#poisson: lambda must be positive");
|
37
|
+
|
38
|
+
mode = floor(lambda);
|
39
|
+
xu = mode;
|
40
|
+
xl = mode - 1;
|
41
|
+
pu = poisson_distribution(mode, lambda);
|
42
|
+
pl = pu * backward_ratio(xu, lambda);
|
43
|
+
u = rb_random_real(self);
|
44
|
+
|
45
|
+
for (;;) {
|
46
|
+
if (u <= pu)
|
47
|
+
return INT2NUM(xu);
|
48
|
+
u = u - pu;
|
49
|
+
pu *= forward_ratio(xu, lambda);
|
50
|
+
++xu;
|
51
|
+
|
52
|
+
if (xl >= 0) {
|
53
|
+
if (u <= pl)
|
54
|
+
return INT2NUM(xl);
|
55
|
+
u = u - pl;
|
56
|
+
pl *= backward_ratio(xl, lambda);
|
57
|
+
--xl;
|
58
|
+
}
|
59
|
+
}
|
60
|
+
}
|
61
|
+
|
62
|
+
void randomext_poisson_init(VALUE cRandom)
|
63
|
+
{
|
64
|
+
rb_define_method(cRandom, "poisson", random_poisson_inv, 1);
|
65
|
+
}
|
data/ext/randomext.h
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
#include <ruby.h>
|
2
|
+
#include <math.h>
|
3
|
+
#include <stdint.h>
|
4
|
+
|
5
|
+
double randomext_random_standard_normal(VALUE random);
|
6
|
+
double randomext_sumlog(int from, int to);
|
7
|
+
double randomext_logcombination(int n, int m);
|
8
|
+
|
9
|
+
inline static uint64_t pow2(int r)
|
10
|
+
{
|
11
|
+
return (uint64_t)1<<r;
|
12
|
+
}
|
13
|
+
|
14
|
+
inline static double random_open_interval(VALUE random)
|
15
|
+
{
|
16
|
+
for (;;) {
|
17
|
+
double u = rb_random_real(random);
|
18
|
+
if (u != 0.0) return u;
|
19
|
+
}
|
20
|
+
}
|
21
|
+
|
22
|
+
#define MASK(bits) (~(~0<<(bits)))
|
23
|
+
#define BIT(nth) (1<<(nth))
|
24
|
+
|
25
|
+
#define MAX2(n, m) (((n) < (m)) ? (m) : (n))
|
26
|
+
#define MIN2(n, m) (((n) < (m)) ? (n) : (m))
|
27
|
+
|
@@ -0,0 +1,76 @@
|
|
1
|
+
#include "randomext.h"
|
2
|
+
|
3
|
+
#define SUMLOG_TABLE_SIZE_INIT 1024
|
4
|
+
#define SUMLOG_TABLE_SIZE_MAX (1024*32)
|
5
|
+
|
6
|
+
/* sumlog_table[0] = 0 */
|
7
|
+
/* sumlog_table[i] = log(1) + log(2) + ... + log(i) */
|
8
|
+
static double* sumlog_table = NULL;
|
9
|
+
static int sumlog_table_size = -1;
|
10
|
+
|
11
|
+
static inline void setup_sumlog_table(int need)
|
12
|
+
{
|
13
|
+
int old_sumlog_table_size;
|
14
|
+
int i;
|
15
|
+
|
16
|
+
if (sumlog_table_size > need || need >= SUMLOG_TABLE_SIZE_MAX)
|
17
|
+
return;
|
18
|
+
|
19
|
+
if (sumlog_table == NULL) {
|
20
|
+
sumlog_table_size = SUMLOG_TABLE_SIZE_INIT;
|
21
|
+
sumlog_table = ALLOC_N(double, sumlog_table_size);
|
22
|
+
sumlog_table[0] = 0;
|
23
|
+
old_sumlog_table_size = 1;
|
24
|
+
} else {
|
25
|
+
old_sumlog_table_size = sumlog_table_size;
|
26
|
+
}
|
27
|
+
|
28
|
+
for (;sumlog_table_size < need; sumlog_table_size <<= 1)
|
29
|
+
;
|
30
|
+
|
31
|
+
REALLOC_N(sumlog_table, double, sumlog_table_size);
|
32
|
+
|
33
|
+
for (i = old_sumlog_table_size; i < sumlog_table_size; ++i) {
|
34
|
+
sumlog_table[i] = sumlog_table[i-1] + log(i);
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
38
|
+
double randomext_sumlog(int from, int to)
|
39
|
+
{
|
40
|
+
int i;
|
41
|
+
double ret = 0.0;
|
42
|
+
|
43
|
+
setup_sumlog_table(to);
|
44
|
+
if (to < SUMLOG_TABLE_SIZE_MAX)
|
45
|
+
return sumlog_table[to] - sumlog_table[from-1];
|
46
|
+
|
47
|
+
for (i=from; i<=to; ++i)
|
48
|
+
ret += log(i);
|
49
|
+
return ret;
|
50
|
+
}
|
51
|
+
|
52
|
+
double randomext_logcombination(int n, int m)
|
53
|
+
{
|
54
|
+
return randomext_sumlog(n-m+1, n) - randomext_sumlog(1, m);
|
55
|
+
}
|
56
|
+
|
57
|
+
extern void randomext_standard_normal_init(VALUE cRandom);
|
58
|
+
extern void randomext_standard_exponential_init(VALUE cRandom);
|
59
|
+
extern void randomext_gamma_init(VALUE cRandom);
|
60
|
+
extern void randomext_binomial_init(VALUE cRandom);
|
61
|
+
extern void randomext_poisson_init(VALUE cRandom);
|
62
|
+
extern void randomext_hypergeometric_init(VALUE cRandom);
|
63
|
+
extern void randomext_other_init(VALUE cRandom);
|
64
|
+
|
65
|
+
void Init_randomext_native()
|
66
|
+
{
|
67
|
+
VALUE cRandom = rb_const_get(rb_cObject, rb_intern("Random"));
|
68
|
+
|
69
|
+
randomext_standard_normal_init(cRandom);
|
70
|
+
randomext_standard_exponential_init(cRandom);
|
71
|
+
randomext_gamma_init(cRandom);
|
72
|
+
randomext_binomial_init(cRandom);
|
73
|
+
randomext_poisson_init(cRandom);
|
74
|
+
randomext_hypergeometric_init(cRandom);
|
75
|
+
randomext_other_init(cRandom);
|
76
|
+
}
|
@@ -0,0 +1,79 @@
|
|
1
|
+
#include "randomext.h"
|
2
|
+
|
3
|
+
#define K 8
|
4
|
+
#define n (1<<K)
|
5
|
+
#define v 0.00394965982258
|
6
|
+
#define r 7.697117470131
|
7
|
+
#define m 64
|
8
|
+
|
9
|
+
static double* w = NULL;
|
10
|
+
static double* f;
|
11
|
+
static uint64_t* k;
|
12
|
+
|
13
|
+
void init_exponentail_table(void)
|
14
|
+
{
|
15
|
+
double xi;
|
16
|
+
int i;
|
17
|
+
|
18
|
+
w = ALLOC_N(double, n);
|
19
|
+
f = ALLOC_N(double, n);
|
20
|
+
k = ALLOC_N(uint64_t, n);
|
21
|
+
|
22
|
+
w[n-1] = v*exp(r)/pow2(m-K);
|
23
|
+
w[n-2] = r/pow2(m-K);
|
24
|
+
k[n-1] = floor(r/w[n-1]);
|
25
|
+
f[n-1] = exp(-r);
|
26
|
+
xi = r;
|
27
|
+
|
28
|
+
for (i=n-2; i >= 1; --i) {
|
29
|
+
xi = -log(exp(-xi) + v/xi);
|
30
|
+
w[i-1] = xi/pow2(m-K);
|
31
|
+
k[i] = floor(xi/w[i]);
|
32
|
+
f[i] = exp(-xi);
|
33
|
+
}
|
34
|
+
k[0] = 0;
|
35
|
+
f[0] = 1;
|
36
|
+
}
|
37
|
+
|
38
|
+
static double standard_exponential(VALUE rng)
|
39
|
+
{
|
40
|
+
uint32_t u1;
|
41
|
+
uint32_t i;
|
42
|
+
uint64_t u2;
|
43
|
+
|
44
|
+
if (w == NULL)
|
45
|
+
init_exponentail_table();
|
46
|
+
|
47
|
+
retry:
|
48
|
+
u1 = rb_random_int32(rng);
|
49
|
+
i = MASK(K) & u1;
|
50
|
+
u2 = (uint64_t)rb_random_int32(rng) | (((uint64_t)u1 >> K) << 32);
|
51
|
+
|
52
|
+
if (u2 < k[i])
|
53
|
+
return (double)u2 * w[i];
|
54
|
+
if (i == n-1) {
|
55
|
+
double u = rb_random_real(rng);
|
56
|
+
return r - log(1-u);
|
57
|
+
} else {
|
58
|
+
double ux = u2*w[i];
|
59
|
+
double u = rb_random_real(rng);
|
60
|
+
if (u*(f[i]-f[i+1]) <= exp(-ux) - f[i+1])
|
61
|
+
return ux;
|
62
|
+
goto retry;
|
63
|
+
}
|
64
|
+
}
|
65
|
+
|
66
|
+
/*
|
67
|
+
* Draws a random sample from the standard exponential distribution.
|
68
|
+
*
|
69
|
+
* @return [Float] a random sample
|
70
|
+
*/
|
71
|
+
static VALUE random_standard_exp(VALUE self)
|
72
|
+
{
|
73
|
+
return DBL2NUM(standard_exponential(self));
|
74
|
+
}
|
75
|
+
|
76
|
+
void randomext_standard_exponential_init(VALUE cRandom)
|
77
|
+
{
|
78
|
+
rb_define_method(cRandom, "standard_exponential", random_standard_exp, 0);
|
79
|
+
}
|
@@ -0,0 +1,94 @@
|
|
1
|
+
#include "randomext.h"
|
2
|
+
|
3
|
+
#define R 3.442619855899
|
4
|
+
#define V 9.91256303526217e-3
|
5
|
+
#define K 7
|
6
|
+
#define M 64
|
7
|
+
#define N (1<<K)
|
8
|
+
|
9
|
+
static double* w = NULL;
|
10
|
+
static uint64_t* k;
|
11
|
+
static double* f;
|
12
|
+
|
13
|
+
inline static double sn(double x)
|
14
|
+
{
|
15
|
+
return exp(-x*x/2);
|
16
|
+
}
|
17
|
+
|
18
|
+
static void init_snormal_table(void)
|
19
|
+
{
|
20
|
+
int i;
|
21
|
+
double xi;
|
22
|
+
|
23
|
+
w = ALLOC_N(double, N);
|
24
|
+
k = ALLOC_N(uint64_t, N);
|
25
|
+
f = ALLOC_N(double, N);
|
26
|
+
|
27
|
+
w[N-1] = V*exp(R*R/2)/pow2(M-K-1);
|
28
|
+
w[N-2] = R/pow2(M-K-1);
|
29
|
+
k[N-1] = floor(R/w[N-1]);
|
30
|
+
f[N-1] = sn(R);
|
31
|
+
xi = R;
|
32
|
+
|
33
|
+
for (i=N-2; i>=1; --i) {
|
34
|
+
xi = sqrt(-2*log(sn(xi)+V/xi));
|
35
|
+
w[i-1] = xi/pow2(M-K-1);
|
36
|
+
k[i] = floor(xi/w[i]);
|
37
|
+
f[i] = sn(xi);
|
38
|
+
}
|
39
|
+
k[0] = 0;
|
40
|
+
f[0] = 1;
|
41
|
+
}
|
42
|
+
|
43
|
+
static double sample_from_tail(VALUE random)
|
44
|
+
{
|
45
|
+
for (;;) {
|
46
|
+
double x = sqrt(R*R-2*log(1-rb_random_real(random)));
|
47
|
+
if (x*rb_random_real(random) <= R)
|
48
|
+
return x;
|
49
|
+
}
|
50
|
+
}
|
51
|
+
|
52
|
+
double randomext_random_standard_normal(VALUE random)
|
53
|
+
{
|
54
|
+
int i;
|
55
|
+
uint64_t u;
|
56
|
+
int sign;
|
57
|
+
double ux;
|
58
|
+
|
59
|
+
if (w == NULL)
|
60
|
+
init_snormal_table();
|
61
|
+
|
62
|
+
for (;;) {
|
63
|
+
unsigned int u0 = rb_random_int32(random);
|
64
|
+
i = u0 & MASK(K);
|
65
|
+
sign = (u0 & BIT(K)) ? 1 : -1;
|
66
|
+
u = ((uint64_t)(u0 >> (K+1)) << 32) | rb_random_int32(random);
|
67
|
+
|
68
|
+
if (u < k[i])
|
69
|
+
return sign*(u*w[i]);
|
70
|
+
if (i == N-1)
|
71
|
+
return sign*sample_from_tail(random);
|
72
|
+
ux = u * w[i];
|
73
|
+
if ( rb_random_real(random)*(f[i]-f[i+1]) <= sn(ux)-f[i+1])
|
74
|
+
return sign*ux;
|
75
|
+
}
|
76
|
+
|
77
|
+
}
|
78
|
+
|
79
|
+
/*
|
80
|
+
* Draws a random sample from the standard normal distribution.
|
81
|
+
*
|
82
|
+
* Ziggurat method is used for random sampling.
|
83
|
+
*
|
84
|
+
* @return [Float] a random sample
|
85
|
+
*/
|
86
|
+
static VALUE random_standard_normal(VALUE self)
|
87
|
+
{
|
88
|
+
return DBL2NUM(randomext_random_standard_normal(self));
|
89
|
+
}
|
90
|
+
|
91
|
+
void randomext_standard_normal_init(VALUE cRandom)
|
92
|
+
{
|
93
|
+
rb_define_method(cRandom, "standard_normal", random_standard_normal, 0);
|
94
|
+
}
|
data/lib/randomext.rb
ADDED
@@ -0,0 +1,373 @@
|
|
1
|
+
require 'randomext_native'
|
2
|
+
|
3
|
+
class Random
|
4
|
+
# Draws a random sample from a normal(Gaussian) distribution.
|
5
|
+
#
|
6
|
+
# @param [Float] mean mean
|
7
|
+
# @param [Float] sd standard deviarion
|
8
|
+
# @return [Float] a random sample
|
9
|
+
def normal(mean=0.0, sd=1.0)
|
10
|
+
mean + standard_normal()*sd
|
11
|
+
end
|
12
|
+
|
13
|
+
# Draws a random sample from a log normal distribution.
|
14
|
+
#
|
15
|
+
# The probabilistic mass function of lognormal distribution is defined:
|
16
|
+
#
|
17
|
+
# 1/sqrt(2*PI*sigma**2)*exp(-(log(x)-mu)**2/(2*sigma**2))
|
18
|
+
#
|
19
|
+
# @param [Float] mu the mean in a normal distribution
|
20
|
+
# @param [Float] sigma the standard deviarion in a normal distribution
|
21
|
+
# @return [Float] a random sample in (0, INFINITY)
|
22
|
+
def lognormal(mu=0.0, sigma=1.0)
|
23
|
+
Math.exp(normal(mu, sigma))
|
24
|
+
end
|
25
|
+
|
26
|
+
# Draws a random sample from a Cauthy distribution.
|
27
|
+
#
|
28
|
+
# @param [Float] loc location parameter
|
29
|
+
# @param [Float] scale scale parameter
|
30
|
+
# @return [Float] a random sample
|
31
|
+
def cauthy(loc, scale)
|
32
|
+
loc + scale*standard_cauthy()
|
33
|
+
end
|
34
|
+
|
35
|
+
# Draws a random sample from the standard Cauthy distribution.
|
36
|
+
#
|
37
|
+
# Computed using Polar method from the standard normal distribution.
|
38
|
+
# @return [Float] a random sample
|
39
|
+
def standard_cauthy
|
40
|
+
y1 = standard_normal()
|
41
|
+
begin; y2 = standard_normal(); end until y2 != 0.0
|
42
|
+
return y1/y2
|
43
|
+
end
|
44
|
+
|
45
|
+
# Draws a random sample from a Levy distribution.
|
46
|
+
#
|
47
|
+
# @param [Float] loc location parameter
|
48
|
+
# @param [Float] scale scale parameter
|
49
|
+
# @return [Float] a random sample
|
50
|
+
def levy(loc=0.0, scale=1.0)
|
51
|
+
begin z = standard_normal.abs; end until z > 0
|
52
|
+
loc + scale/z**2
|
53
|
+
end
|
54
|
+
|
55
|
+
# Draws a random sample from a exponential distribution.
|
56
|
+
#
|
57
|
+
# Inverse function method is used.
|
58
|
+
# @param [Float] scale scale parameter (scale > 0)
|
59
|
+
# @return [Float] a random sample
|
60
|
+
def exponential(scale=1.0)
|
61
|
+
if scale < 0.0
|
62
|
+
raise ArgumentError, "Random#exponential: scale parameter must be positive"
|
63
|
+
end
|
64
|
+
scale * standard_exponential
|
65
|
+
end
|
66
|
+
|
67
|
+
# Draws a random sample from a Laplace distribution
|
68
|
+
#
|
69
|
+
# @param [Float] loc location parameter
|
70
|
+
# @param [Float] scale scale parameter
|
71
|
+
# @return [Float] a random sample
|
72
|
+
def laplace(loc=0.0, scale=1.0)
|
73
|
+
sign = rand(2) == 1 ? 1 : -1
|
74
|
+
loc + sign*scale*standard_exponential
|
75
|
+
end
|
76
|
+
|
77
|
+
# Draws a random sample from a Rayleigh distribution
|
78
|
+
#
|
79
|
+
# @param [Float] sigma scale parameter
|
80
|
+
# @return [Float] a random sample
|
81
|
+
def rayleigh(sigma=1.0)
|
82
|
+
sigma*Math.sqrt(2*standard_exponential)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Draws a random sample from a Weibull distribution
|
86
|
+
#
|
87
|
+
# @param [Float] g shape parameter (g > 0.0)
|
88
|
+
# @param [Float] mu scale parameter
|
89
|
+
# @return [Float] a random sample
|
90
|
+
def weibull(g, mu=1.0)
|
91
|
+
if g <= 0
|
92
|
+
raise ArgumentError, "Random#weibull: shape parameter must be positive"
|
93
|
+
end
|
94
|
+
mu * standard_exponential**(1.0/g)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Draws a random sample from a Gumbel distribution
|
98
|
+
#
|
99
|
+
# @param [Float] loc location parameter
|
100
|
+
# @param [Float] scale scale parameter
|
101
|
+
# @return [Float] a random sample
|
102
|
+
def gumbel(loc=0.0, scale=1.0)
|
103
|
+
loc - scale * Math.log(standard_exponential)
|
104
|
+
end
|
105
|
+
|
106
|
+
# Draws a random sample from a gamma distribution
|
107
|
+
#
|
108
|
+
# @param [Float] shape shape parameter (shape > 0.0)
|
109
|
+
# @param [Float] scale scale parameter (scale > 0.0)
|
110
|
+
# @return [Float] a random sample
|
111
|
+
def gamma(shape, scale=1.0)
|
112
|
+
if scale <= 0.0
|
113
|
+
raise ArgumentError, "Random#gamma: scale parameter must be positive"
|
114
|
+
end
|
115
|
+
|
116
|
+
case
|
117
|
+
when shape <= 0.0
|
118
|
+
raise ArgumentError, "Random#gamma: shape parameter must be positive"
|
119
|
+
when shape > 1.0
|
120
|
+
scale * _gamma(shape)
|
121
|
+
when shape == 1.0
|
122
|
+
exponential(scale)
|
123
|
+
when shape < 1.0
|
124
|
+
scale*_gamma(shape+1)*rand_open_interval**(1.0/shape)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# Draws a random sample from a beta distribution.
|
129
|
+
#
|
130
|
+
# @param [Float] alpha a shape parameter (alpha > 0.0)
|
131
|
+
# @param [Float] beta another shape parameter (beta > 0.0)
|
132
|
+
# @return [Float] a random sample
|
133
|
+
def beta(alpha, beta)
|
134
|
+
y1 = gamma(alpha); y2 = gamma(beta)
|
135
|
+
y1/(y1+y2)
|
136
|
+
end
|
137
|
+
|
138
|
+
# Draws a random sample from a Dirichlet distribution.
|
139
|
+
#
|
140
|
+
# @param [Array<Float>] as shape parameters
|
141
|
+
# @return [Array<Float>] a random sample in the (K-1)-dimensional simplex (K == as.size)
|
142
|
+
def dirichlet(*as)
|
143
|
+
if as.any?{|a| a <= 0.0}
|
144
|
+
raise ArgumentError, "Random#dirichlet: parameters must be positive"
|
145
|
+
end
|
146
|
+
|
147
|
+
ys = as.map{|a| gamma(a) }
|
148
|
+
sum = ys.inject(0.0, &:+)
|
149
|
+
ys.map{|y| y/sum }
|
150
|
+
end
|
151
|
+
|
152
|
+
# Draws a random sample from a power function distribution
|
153
|
+
#
|
154
|
+
# @param [Float] shape shape parameter (shape > 0.0)
|
155
|
+
# @param [Float] a lower boundary parameter
|
156
|
+
# @param [Float] b upper boundary parameter (a < b)
|
157
|
+
# @return [Float] a random sample
|
158
|
+
def power(shape, a, b)
|
159
|
+
if shape <= 0 || a >= b
|
160
|
+
raise ArgumentError, "Random#power: shape must be positive, and b should be greater than a"
|
161
|
+
end
|
162
|
+
|
163
|
+
a + (b-a)*(rand_open_interval**(1/shape))
|
164
|
+
end
|
165
|
+
# Draw a random sample from a chi_square distribution.
|
166
|
+
#
|
167
|
+
# @param [Integer] r degree of freedom (r >= 1)
|
168
|
+
# @return [Float] a random sample
|
169
|
+
def chi_square(r)
|
170
|
+
if r == 1
|
171
|
+
standard_normal ** 2
|
172
|
+
elsif r > 1
|
173
|
+
gamma(r*0.5, 2)
|
174
|
+
else
|
175
|
+
raise ArgumentError, "Random#chi_square:r (degree of freedom) must be >= 1"
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
# Draws a random sample from a F distribution.
|
180
|
+
#
|
181
|
+
# @param [Integer] r1 degree of freedom (r1 >= 1)
|
182
|
+
# @param [Integer] r2 degree of freedom (r2 >= 1)
|
183
|
+
# @return [Float] a random sample
|
184
|
+
def F(r1, r2)
|
185
|
+
f = r2 / r1.to_f
|
186
|
+
f*chi_square(r1)/chi_square(r2)
|
187
|
+
end
|
188
|
+
|
189
|
+
# Draws a random sample from a t distribution.
|
190
|
+
#
|
191
|
+
# @param [Integer] r degree of freedom (r >= 1)
|
192
|
+
# @return [Float] a random sample
|
193
|
+
def t(r)
|
194
|
+
if r ==1
|
195
|
+
standard_cauthy
|
196
|
+
elsif r == 2
|
197
|
+
standard_normal/Math.sqrt(exponential(1))
|
198
|
+
elsif r > 2
|
199
|
+
rdiv2 = r/2.0
|
200
|
+
Math.sqrt(rdiv2)*standard_normal/Math.sqrt(_gamma(rdiv2))
|
201
|
+
else
|
202
|
+
raise ArgumentError, "Random#t: r (degree of freedom) must be >= 1"
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
# Draws a random sample from a wald distribution.
|
207
|
+
#
|
208
|
+
# A wald distribution is also called an inverse Gaussian distribution.
|
209
|
+
#
|
210
|
+
# @param [Float] mean mean
|
211
|
+
# @param [Float] shape shape parameter (shape > 0.0)
|
212
|
+
# @return [Float] a random sample in (0, INFINITY)
|
213
|
+
def wald(mean, shape)
|
214
|
+
if shape <= 0.0
|
215
|
+
raise ArgumentError, "Random#wald: shape parameter must be positive"
|
216
|
+
end
|
217
|
+
p = mean**2
|
218
|
+
q = p/(2*shape)
|
219
|
+
z = standard_normal
|
220
|
+
return mean if z == 0.0
|
221
|
+
v = mean + q*z**2
|
222
|
+
x1 = v + Math.sqrt(v**2-p)
|
223
|
+
return x1 if rand*(x1 + mean) <= mean
|
224
|
+
return p/x1
|
225
|
+
end
|
226
|
+
|
227
|
+
# Draws a random sample from a Pareto distribution.
|
228
|
+
#
|
229
|
+
# The probabilistic mass function for the distribution is defined as:
|
230
|
+
#
|
231
|
+
# p(x) = a*b**a/x**(a+1)
|
232
|
+
#
|
233
|
+
# @param [Float] a shape parameter (a > 0.0)
|
234
|
+
# @param [Float] b scale parameter (b > 0.0)
|
235
|
+
# @return [Float] a random sample in [b, INFINITY)
|
236
|
+
def pareto(a, b=1.0)
|
237
|
+
if a <= 0 || b <= 0
|
238
|
+
raise ArgumentError, "Random#pareto: parameters a and b must be positive"
|
239
|
+
end
|
240
|
+
b * (1.0 - rand)**(-1/a)
|
241
|
+
end
|
242
|
+
|
243
|
+
# Draws a random sample from a logistic distribution.
|
244
|
+
#
|
245
|
+
# @param [Float] mu the location parameter
|
246
|
+
# @param [Float] theta the scale parameter
|
247
|
+
# @return [Float] a random sample
|
248
|
+
def logistic(mu, theta)
|
249
|
+
u = rand_open_interval
|
250
|
+
mu + theta*log(u/(1-u))
|
251
|
+
end
|
252
|
+
|
253
|
+
# Draws a random sample from a Non-Central Chi-Square distribution.
|
254
|
+
#
|
255
|
+
# @param [Integer] r a parameter (r > 0)
|
256
|
+
# @param [Float] lambda another parameter (lambda > 0)
|
257
|
+
# @return [Float] a random sample in (0, INFINITY)
|
258
|
+
def non_central_chi_square(r, lambda)
|
259
|
+
if lambda < 0.0
|
260
|
+
raise ArgumentError, "Random#non_central_chi_square: lambda must be positive"
|
261
|
+
end
|
262
|
+
if !r.integer? || r <= 0
|
263
|
+
raise ArgumentError, "Random#non_central_chi_square: r must be positive integer"
|
264
|
+
end
|
265
|
+
j = poisson(lambda/2)
|
266
|
+
chi_square(r + 2*j)
|
267
|
+
end
|
268
|
+
|
269
|
+
# Draws a random sample from a Non-Central t distribution
|
270
|
+
#
|
271
|
+
# @param [Integer] r a parameter (r > 0)
|
272
|
+
# @param [Float] lambda another parameter (lambda > 0)
|
273
|
+
# @return [Float] a random sample
|
274
|
+
def non_central_t(r, lambda)
|
275
|
+
if lambda == 0.0
|
276
|
+
raise ArgumentError, "Random#non_central_t: lambda must not be 0"
|
277
|
+
end
|
278
|
+
|
279
|
+
if r == 1
|
280
|
+
z = standard_normal + lambda
|
281
|
+
w = standard_normal.abs
|
282
|
+
z/w
|
283
|
+
elsif r == 2
|
284
|
+
z = standard_normal + lambda
|
285
|
+
w = standard_exponential
|
286
|
+
z/Math.sqrt(w)
|
287
|
+
elsif r > 2
|
288
|
+
d = Math.sqrt(r/2.0)
|
289
|
+
z = standard_normal + lambda
|
290
|
+
w = _gamma(r/2.0)
|
291
|
+
d*z/Math.sqrt(w)
|
292
|
+
else
|
293
|
+
raise ArgumentError, "Random#non_central_t: r must be positive"
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
# Draw a random sample from a Bernoulli distribution.
|
298
|
+
#
|
299
|
+
# @param [Float] p the probability returning 1
|
300
|
+
# @return [Integer] a random sample, 0 or 1
|
301
|
+
def bernoulli(p)
|
302
|
+
(rand < p) ? 1 : 0
|
303
|
+
end
|
304
|
+
|
305
|
+
# Draws a random sample from a geometric distribution.
|
306
|
+
#
|
307
|
+
# @param [Float] theta the probability of sucess (0 < theta < 1)
|
308
|
+
# @return [Integer] a random sample in [1, INFINITY)
|
309
|
+
def geometric(theta)
|
310
|
+
if theta <= 0.0 || theta >= 1.0
|
311
|
+
raise ArgumentError, "Random#geometric: theta should be in (0, 1)"
|
312
|
+
end
|
313
|
+
|
314
|
+
d= -1/(Math.log(1-theta))
|
315
|
+
(d * standard_exponential).floor + 1
|
316
|
+
end
|
317
|
+
|
318
|
+
# Draws a random sample from a Planck distribution.
|
319
|
+
#
|
320
|
+
# @param [Float] a shape parameter
|
321
|
+
# @param [Float] b scale parameter
|
322
|
+
# @return [Float] a random sample in (0, INFINITY)
|
323
|
+
def planck(a, b)
|
324
|
+
if a <= 0 || b <= 0
|
325
|
+
raise ArgumentError, "Random#planck: parameters must be positive"
|
326
|
+
end
|
327
|
+
|
328
|
+
y = _gamma(a+1)
|
329
|
+
j = zeta(a+1)
|
330
|
+
b*y/j
|
331
|
+
end
|
332
|
+
|
333
|
+
# Draws a random sample from a negative binomial distribution.
|
334
|
+
#
|
335
|
+
# @param [Float] r s parameter (0 < r)
|
336
|
+
# @param [Float] theta a parameter (0 < theta < 1)
|
337
|
+
# @return [Integer] a random sample in [0, INFINITY)
|
338
|
+
def negative_binomial(r, theta)
|
339
|
+
if r <= 0.0
|
340
|
+
raise ArgumentError, "Random#negative_binomial: r must be positive"
|
341
|
+
end
|
342
|
+
if theta <= 0.0 && theta >= 1.0
|
343
|
+
raise ArgumentError, "Random#negative_binomial: theta must be in (0, 1)"
|
344
|
+
end
|
345
|
+
poisson(gamma(r, 1/theta - 1))
|
346
|
+
end
|
347
|
+
|
348
|
+
# Draws a random sample from a log series distribution.
|
349
|
+
#
|
350
|
+
# @param [Float] theta a parameter (0 < theta < 1)
|
351
|
+
# @return [Integer] a random sample in [0, INFINITY)
|
352
|
+
def logseries(theta)
|
353
|
+
if theta <= 0 || 1 <= theta
|
354
|
+
raise ArgumentError, "Random#logseries: theta must be in (0, 1)"
|
355
|
+
end
|
356
|
+
q = 1 - theta
|
357
|
+
v = rand_open_interval
|
358
|
+
if v >= theta
|
359
|
+
1
|
360
|
+
else
|
361
|
+
u = rand_open_interval
|
362
|
+
(log(v)/log(1-q**u)).ceil
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
# Draw a sample from the uniform distribution on (0, 1)
|
367
|
+
#
|
368
|
+
# @return [Float] a random sample in (0, 1)
|
369
|
+
def rand_open_interval
|
370
|
+
begin; x = rand; end until x != 0.0
|
371
|
+
x
|
372
|
+
end
|
373
|
+
end
|
metadata
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: randomext
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Ippei Obayashi
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-11-14 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: ! "This library extends class Random in the Ruby standard library.\nThe
|
15
|
+
Random class in the Ruby standard library supports only \nrandom sampling from discrete/continuous
|
16
|
+
uniform distribution.\n\nThis library provides random sampling methods from \nmany
|
17
|
+
kinds of probability distributions such as normal, gamma,\nbeta, chi_square, t,
|
18
|
+
F, binomial, Poisson, and many other\ndistributions.\n"
|
19
|
+
email: ohai@kmc.gr.jp
|
20
|
+
executables: []
|
21
|
+
extensions:
|
22
|
+
- ext/extconf.rb
|
23
|
+
extra_rdoc_files: []
|
24
|
+
files:
|
25
|
+
- lib/randomext.rb
|
26
|
+
- ext/extconf.rb
|
27
|
+
- ext/standard_exponential.c
|
28
|
+
- ext/randomext.h
|
29
|
+
- ext/other.c
|
30
|
+
- ext/hypergeometric.c
|
31
|
+
- ext/randomext_native.c
|
32
|
+
- ext/poisson.c
|
33
|
+
- ext/binomial.c
|
34
|
+
- ext/gamma.c
|
35
|
+
- ext/standard_normal.c
|
36
|
+
homepage: http://www.kmc.gr.jp/~ohai/randomext/
|
37
|
+
licenses: []
|
38
|
+
post_install_message:
|
39
|
+
rdoc_options: []
|
40
|
+
require_paths:
|
41
|
+
- lib
|
42
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
43
|
+
none: false
|
44
|
+
requirements:
|
45
|
+
- - ! '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
requirements: []
|
55
|
+
rubyforge_project:
|
56
|
+
rubygems_version: 1.8.23
|
57
|
+
signing_key:
|
58
|
+
specification_version: 3
|
59
|
+
summary: This library extend Random class of Ruby standard library
|
60
|
+
test_files: []
|