randomext 0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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: []
|