1 | /* ================================================================
|
2 | * probability-distributions by Matt Asher (me[at]mattasher.com)
|
3 | * Originally created for StatisticsBlog.com
|
4 | *
|
5 | * first created at : Sat Oct 10 2015
|
6 | *
|
7 | * ================================================================
|
8 | * Copyright 2015 Matt Asher
|
9 | *
|
10 | * Licensed under the MIT License
|
11 | * You may not use this file except in compliance with the License.
|
12 | *
|
13 | * ================================================================ */
|
14 |
|
15 | var crypto = require('crypto');
|
16 |
|
17 | // Shortcuts
|
18 | var exp = Math.exp;
|
19 | var ln = Math.log;
|
20 | var PI = Math.PI;
|
21 |
|
22 | module.exports = {
|
23 |
|
24 | /**
|
25 | * This is the core function for generating entropy
|
26 | *
|
27 | * @param len number of bytes of entropy to create
|
28 | * @returns {number} A pseduo random number between 0 and 1
|
29 | *
|
30 | */
|
31 | prng: function(len) {
|
32 | if(len === undefined) len=16;
|
33 |
|
34 | var entropy = crypto.randomBytes(len);
|
35 | var result = 0;
|
36 |
|
37 | for(var i=0; i<len; i++) {
|
38 | result = result + Number(entropy[i])/Math.pow(256,(i+1))
|
39 | }
|
40 | return result
|
41 | },
|
42 |
|
43 |
|
44 |
|
45 |
|
46 | /**
|
47 | *
|
48 | * @param n The number of random variates to create. Must be a positive integer.
|
49 | * @param alpha First shape parameter
|
50 | * @param beta Second shape parameter
|
51 | * @param loc Location or Non-centrality parameter
|
52 | */
|
53 | rbeta: function(n, alpha, beta, loc) {
|
54 | // Uses relationship with gamma to calculate
|
55 |
|
56 | // Validations
|
57 | n = this._v(n, "n");
|
58 | alpha = this._v(alpha, "nn", 1);
|
59 | beta = this._v(beta, "nn", 1);
|
60 | loc = this._v(loc, "r", 0);
|
61 |
|
62 | var toReturn = [];
|
63 |
|
64 | for(var i=0; i<n; i++) {
|
65 | var g1 = this.rgamma(1, alpha, 1)[0];
|
66 | var g2 = this.rgamma(1, beta, 1)[0];
|
67 |
|
68 |
|
69 | toReturn[i] = loc + g1/(g1+g2);
|
70 | }
|
71 | return toReturn
|
72 |
|
73 | },
|
74 |
|
75 |
|
76 | /**
|
77 | *
|
78 | * @param n Number of variates to return.
|
79 | * @param size Number of Bernoulli trials to be summed up. Defaults to 1
|
80 | * @param p Probability of a "success". Defaults to 0.5
|
81 | * @returns {Array} Random variates array
|
82 | */
|
83 | rbinom: function(n, size, p) {
|
84 | n = this._v(n, "n");
|
85 | size = this._v(size, "nni", 1);
|
86 | p = this._v(p, "p", 0.5);
|
87 |
|
88 | var toReturn = [];
|
89 |
|
90 | for(var i=0; i<n; i++) {
|
91 | var result = 0;
|
92 | for(var j=0; j<size; j++) {
|
93 | if(this.prng() < p) {
|
94 | result++
|
95 | }
|
96 | }
|
97 | toReturn[i] = result;
|
98 | }
|
99 | return toReturn
|
100 | },
|
101 |
|
102 |
|
103 | /**
|
104 | *
|
105 | * @param n The number of variates to create
|
106 | * @param loc Location parameter
|
107 | * @param scale Scale parameter
|
108 | * @returns {Array} Random variates array
|
109 | */
|
110 | rcauchy: function(n, loc, scale) {
|
111 | n = this._v(n, "n");
|
112 | loc = this._v(loc, "r", 0);
|
113 | scale = this._v(scale, "nn", 1);
|
114 |
|
115 | var toReturn = [];
|
116 | for(var i=0; i<n; i++) {
|
117 | var x = scale * Math.tan(PI * (this.prng()-0.5))+loc;
|
118 |
|
119 | toReturn[i] = x;
|
120 | }
|
121 |
|
122 | return toReturn
|
123 | },
|
124 |
|
125 | /**
|
126 | *
|
127 | * @param n The number of variates to create
|
128 | * @param df Degrees of freedom for the distribution
|
129 | * @param ncp Non-centrality parameter
|
130 | * @returns {Array} Random variates array
|
131 | */
|
132 | rchisq: function(n, df, ncp) {
|
133 | n = this._v(n, "n");
|
134 | df = this._v(df, "nn");
|
135 | ncp = this._v(ncp, "r", 0);
|
136 |
|
137 | var toReturn = [];
|
138 | for(var i=0; i<n; i++) {
|
139 | // Start at ncp
|
140 | var x = ncp;
|
141 | for(var j=0; j<df; j++) {
|
142 | x = x + Math.pow(this.rnorm(1)[0],2);
|
143 | }
|
144 | toReturn[i] = x;
|
145 | }
|
146 | return toReturn
|
147 | },
|
148 |
|
149 | /**
|
150 | *
|
151 | * @param n The number of random variates to create. Must be a positive integer.
|
152 | * @param rate The rate parameter. Must be a positive number
|
153 | */
|
154 | rexp: function(n, rate) {
|
155 | n = this._v(n, "n");
|
156 | rate = this._v(rate, "pos", 1);
|
157 |
|
158 | var toReturn = [];
|
159 |
|
160 | for(var i=0; i<n; i++) {
|
161 |
|
162 | toReturn[i] = -ln(this.prng())/rate;
|
163 | }
|
164 |
|
165 | return toReturn
|
166 | },
|
167 |
|
168 | /**
|
169 | *
|
170 | * @param n The number of variates to create
|
171 | * @param df1 Degrees of freedom for the first parameter
|
172 | * @param df2 Degrees of freedom for the first parameter
|
173 | * @returns {Array} Random variates array
|
174 | */
|
175 | rf: function(n, df1, df2) {
|
176 | n = this._v(n, "n");
|
177 | df1 = this._v(df1, "nn");
|
178 | df2 = this._v(df2, "nn");
|
179 |
|
180 | var toReturn = [];
|
181 | for(var i=0; i<n; i++) {
|
182 | var num = this.rchisq(1, df1)[0]/df1;
|
183 | var denom = this.rchisq(1, df2)[0]/df2;
|
184 |
|
185 | toReturn[i] = num/denom;
|
186 | }
|
187 |
|
188 | return toReturn
|
189 |
|
190 | },
|
191 |
|
192 |
|
193 | /**
|
194 | *
|
195 | * @param n The number of random variates to create. Must be a positive integer
|
196 | * @param alpha
|
197 | * @param rate
|
198 | * @returns {Array} Random variates array
|
199 | */
|
200 | rgamma: function(n, alpha, rate) {
|
201 | // Adapted from https://github.com/mvarshney/simjs-source/ & scipy
|
202 | n = this._v(n, "n");
|
203 | alpha = this._v(alpha, "nn");
|
204 | rate = this._v(rate, "pos", 1);
|
205 |
|
206 | var LOG4 = ln(4.0);
|
207 | var SG_MAGICCONST = 1.0 + ln(4.5);
|
208 | var beta = 1/rate;
|
209 |
|
210 | var toReturn = [];
|
211 | for(var i = 0; i<n; i++) {
|
212 |
|
213 | /* Based on Python 2.6 source code of random.py.
|
214 | */
|
215 |
|
216 | if (alpha > 1.0) {
|
217 | var ainv = Math.sqrt(2.0 * alpha - 1.0);
|
218 | var bbb = alpha - LOG4;
|
219 | var ccc = alpha + ainv;
|
220 |
|
221 | while (true) {
|
222 | var u1 = this.prng();
|
223 | if ((u1 < 1e-7) || (u > 0.9999999)) {
|
224 | continue;
|
225 | }
|
226 | var u2 = 1.0 - this.prng();
|
227 | var v = ln(u1 / (1.0 - u1)) / ainv;
|
228 | var x = alpha * exp(v);
|
229 | var z = u1 * u1 * u2;
|
230 | var r = bbb + ccc * v - x;
|
231 | if ((r + SG_MAGICCONST - 4.5 * z >= 0.0) || (r >= ln(z))) {
|
232 | var result = x * beta;
|
233 | break;
|
234 | }
|
235 | }
|
236 | } else if (alpha == 1.0) {
|
237 | var u = this.prng();
|
238 | while (u <= 1e-7) {
|
239 | u = this.prng();
|
240 | }
|
241 | var result = - ln(u) * beta;
|
242 | } else {
|
243 | while (true) {
|
244 | var u = this.prng();
|
245 | var b = (Math.E + alpha) / Math.E;
|
246 | var p = b * u;
|
247 | if (p <= 1.0) {
|
248 | var x = Math.pow(p, 1.0 / alpha);
|
249 | } else {
|
250 | var x = - ln((b - p) / alpha);
|
251 | }
|
252 | var u1 = this.prng();
|
253 | if (p > 1.0) {
|
254 | if (u1 <= Math.pow(x, (alpha - 1.0))) {
|
255 | break;
|
256 | }
|
257 | } else if (u1 <= exp(-x)) {
|
258 | break;
|
259 | }
|
260 | }
|
261 | var result = x * beta;
|
262 | }
|
263 |
|
264 | toReturn[i] = result;
|
265 | }
|
266 |
|
267 | return toReturn;
|
268 |
|
269 | },
|
270 |
|
271 | // Syntax as in R library VGAM
|
272 | /**
|
273 | *
|
274 | * @param n The number of random variates to create. Must be a positive integer
|
275 | * @param loc Mean
|
276 | * @param scale Scale parameter
|
277 | * @returns {Array} Random variates array
|
278 | */
|
279 | rlaplace: function(n, loc, scale) {
|
280 | n = this._v(n, "n");
|
281 | loc = this._v(loc, "r", 0);
|
282 | scale = this._v(scale, "nn", 1);
|
283 |
|
284 | var toReturn = [];
|
285 |
|
286 | for(var i=0; i<n; i++) {
|
287 | var core = this.sample([-1,1])[0] * ln(this.prng());
|
288 |
|
289 | var x = loc - scale * core;
|
290 |
|
291 | toReturn[i] = x;
|
292 | }
|
293 |
|
294 | return toReturn
|
295 | },
|
296 |
|
297 |
|
298 | /**
|
299 | *
|
300 | * @param n The number of random variates to create. Must be a positive integer.
|
301 | * @param meanlog The mean log.
|
302 | * @param sdlog Log SD. Must be greater than 0.
|
303 | * @returns {Array} Random variates array
|
304 | */
|
305 | rlnorm: function(n, meanlog, sdlog) {
|
306 | n = this._v(n, "n");
|
307 | meanlog = this._v(meanlog, "r", 0);
|
308 | sdlog = this._v(sdlog, "nn", 1);
|
309 |
|
310 | var toReturn = [];
|
311 |
|
312 | for(var i=0; i<n; i++) {
|
313 | var x = this.rnorm(1, meanlog, sdlog)[0];
|
314 |
|
315 | toReturn[i] = exp(x);
|
316 | }
|
317 |
|
318 | return toReturn
|
319 | },
|
320 |
|
321 | /**
|
322 | *
|
323 | * @param n The number of random variates to create. Must be a positive integer.
|
324 | * @param size Number of hits required
|
325 | * @param p Hit probability
|
326 | * @param mu Optional way to specify hit probability
|
327 | * @returns {Array} Random variates array
|
328 | */
|
329 | rnbinom: function(n, size, p, mu) {
|
330 | n = this._v(n, "n");
|
331 | if(size === undefined) size=1;
|
332 | if(Math.round(size) != size) throw "Size must be a whole number";
|
333 | if(size < 1) throw "Size must be one or greater";
|
334 | if(p !== undefined && mu !== undefined) throw "You must specify probability or mean, not both";
|
335 | if(mu !== undefined) p = size/(size+mu);
|
336 | p = this._v(p, "p");
|
337 |
|
338 |
|
339 | var toReturn = [];
|
340 |
|
341 | for(var i=0; i<n; i++) {
|
342 |
|
343 | // Core distribution
|
344 | var result = 0;
|
345 | var leftToFind = size;
|
346 | while(leftToFind > 0) {
|
347 | result++;
|
348 | if(this.prng() < p) leftToFind--;
|
349 | }
|
350 |
|
351 | toReturn[i] = result - 1;
|
352 | }
|
353 |
|
354 | return toReturn
|
355 |
|
356 | },
|
357 |
|
358 | /**
|
359 | *
|
360 | * @param x Where sample the density
|
361 | * @param mean Mean of the distribution
|
362 | * @param sd Standard deviation for the distribution
|
363 | * @returns {Number} The density given the parameter values
|
364 | */
|
365 | dnorm: function(x, mean, sd) {
|
366 | x = this._v(x, "r");
|
367 | mean = this._v(mean, "r", 0);
|
368 | sd = this._v(sd, "nn", 1);
|
369 |
|
370 | // Check for degeneracy
|
371 | if(sd === 0) {
|
372 | if(x === mean) return Infinity;
|
373 | return 0
|
374 | }
|
375 |
|
376 | var a = sd*(Math.sqrt(2*PI));
|
377 | var b = -(x-mean)*(x-mean);
|
378 | var c = 2*sd*sd;
|
379 |
|
380 | return (1/a)*exp(b/c)
|
381 | },
|
382 |
|
383 | /**
|
384 | *
|
385 | * @param n The number of random variates to create. Must be a positive integer.
|
386 | * @param mean Mean of the distribution
|
387 | * @param sd Standard Deviation of the distribution
|
388 | * @returns {Array} Random variates array
|
389 | */
|
390 | rnorm: function(n, mean, sd) {
|
391 | // Adapted from http://blog.yjl.im/2010/09/simulating-normal-random-variable-using.html
|
392 |
|
393 | n = this._v(n, "n");
|
394 | mean = this._v(mean, "r", 0);
|
395 | sd = this._v(sd, "nn", 1);
|
396 |
|
397 | var toReturn = [];
|
398 |
|
399 | for(var i=0; i<n; i++) {
|
400 | var V1, V2, S, X;
|
401 |
|
402 | do {
|
403 | var U1 = this.prng();
|
404 | var U2 = this.prng();
|
405 | V1 = (2 * U1) - 1;
|
406 | V2 = (2 * U2) - 1;
|
407 | S = (V1 * V1) + (V2 * V2);
|
408 | } while (S > 1);
|
409 |
|
410 | X = Math.sqrt(-2 * ln(S) / S) * V1;
|
411 | X = mean + sd * X;
|
412 | toReturn.push(X);
|
413 | }
|
414 |
|
415 | return toReturn
|
416 | },
|
417 |
|
418 | /**
|
419 | *
|
420 | * @param n The number of random variates to create. Must be a positive integer.
|
421 | * @param lambda Mean/Variance of the distribution
|
422 | * @returns {Array} Random variates array
|
423 | */
|
424 | rpois: function(n, lambda) {
|
425 | n = this._v(n, "n");
|
426 | lambda = this._v(lambda, "pos");
|
427 |
|
428 | var toReturn = [];
|
429 |
|
430 | for(var i=0; i<n; i++) {
|
431 |
|
432 | // Adapted from http://wiki.q-researchsoftware.com/wiki/How_to_Generate_Random_Numbers:_Poisson_Distribution
|
433 | if (lambda < 30) {
|
434 |
|
435 | var L = exp(-lambda);
|
436 | var p = 1;
|
437 | var k = 0;
|
438 | do {
|
439 | k++;
|
440 | p *= this.prng();
|
441 | } while (p > L);
|
442 | toReturn.push(k - 1);
|
443 |
|
444 | } else {
|
445 |
|
446 | // Roll our own
|
447 | // Fix total number of samples
|
448 | var samples = 10000;
|
449 | var p = lambda/samples;
|
450 | var k = 0;
|
451 | for(var j=0; j<samples; j++) {
|
452 | if(this.prng() < p) {
|
453 | k++
|
454 | }
|
455 | }
|
456 | toReturn[i] = k;
|
457 | }
|
458 | }
|
459 |
|
460 | return toReturn
|
461 | },
|
462 |
|
463 | /**
|
464 | *
|
465 | * @param n Number of variates to return
|
466 | * @param min Lower bound
|
467 | * @param max Upper bound
|
468 | * @returns {Array} Random variates array
|
469 | */
|
470 | runif: function(n, min, max) {
|
471 | n = this._v(n, "n");
|
472 | min = this._v(min, "r", 0);
|
473 | max = this._v(max, "r", 1);
|
474 | if(min > max) throw "Minimum value cannot be greater than maximum value";
|
475 |
|
476 | var toReturn = [];
|
477 |
|
478 | for(var i=0; i<n; i++) {
|
479 | var raw = this.prng();
|
480 | var scaled = min + raw*(max-min);
|
481 | toReturn.push(scaled)
|
482 | }
|
483 | return toReturn
|
484 | },
|
485 |
|
486 |
|
487 | /**
|
488 | *
|
489 | * @param collection Array of items to sample from
|
490 | * @param n Number of items to sample. If missing, n will be set to the length of the collection and it will shuffle
|
491 | * @param replace Sample with replacement? False by default
|
492 | * @param ratios Ratios to weight items. Can be any non-negative number. By default all items are given equal weight
|
493 | * @returns {Array} Array of sampled items
|
494 | */
|
495 | sample: function(collection, n, replace, ratios) {
|
496 |
|
497 | // Validations
|
498 | collection = this._v(collection, "a");
|
499 | n = this._v(n, "n", collection.length); // If n is undefined, sample the full array
|
500 | if(replace === undefined) replace = false;
|
501 | if(!replace && collection.length < n)
|
502 | throw "You cannot select " + n + " items from an array of length " + collection.length + " without replacement";
|
503 |
|
504 | if(ratios === undefined) {
|
505 | ratios = [];
|
506 | for(var m=0; m<collection.length; m++) { ratios[m] = 1 }
|
507 | }
|
508 |
|
509 | var cumulativeProbs = this._getCumulativeProbs(ratios, collection.length);
|
510 |
|
511 | // Main loop
|
512 | var toReturn = [];
|
513 |
|
514 | for(var i=0; i<n; i++) {
|
515 |
|
516 | var chosen = this._sampleOneIndex(cumulativeProbs);
|
517 |
|
518 | if(replace) {
|
519 | toReturn[i] = collection[chosen];
|
520 | } else {
|
521 |
|
522 | // Remove from collection and ratios
|
523 | toReturn[i] = collection.splice(chosen, 1)[0];
|
524 | ratios.splice(chosen, 1);
|
525 |
|
526 | // Make sure we aren't at the end
|
527 | if(ratios.length) {
|
528 | cumulativeProbs = this._getCumulativeProbs(ratios, collection.length);
|
529 | }
|
530 | }
|
531 | }
|
532 |
|
533 | return toReturn;
|
534 |
|
535 | },
|
536 |
|
537 |
|
538 | // HELPERS
|
539 |
|
540 | /**
|
541 | *
|
542 | * @param ratios Array of non-negative numbers to be turned into CDF
|
543 | * @param len length of the collection
|
544 | * @returns {Array}
|
545 | * @private
|
546 | */
|
547 | _getCumulativeProbs: function(ratios, len) {
|
548 | if(len === undefined) throw "An error occurred: len was not sent to _getCumulativeProbs";
|
549 | if(ratios.length !== len) throw "Probabilities for sample must be same length as the array to sample from";
|
550 |
|
551 | var toReturn = [];
|
552 |
|
553 | if(ratios !== undefined) {
|
554 | ratios = this._v(ratios, "a");
|
555 | if(ratios.length !== len) throw "Probabilities array must be the same length as the array you are sampling from";
|
556 |
|
557 | var sum = 0;
|
558 | ratios.map(function(ratio) {
|
559 | ratio = this._v(ratio, "nn"); // Note validating as ANY non-negative number
|
560 | sum+= ratio;
|
561 | toReturn.push(sum);
|
562 | }.bind(this));
|
563 |
|
564 | // Divide by total to normalize
|
565 | for(var k=0; k<toReturn.length; k++) { toReturn[k] = toReturn[k]/sum }
|
566 | return toReturn
|
567 | }
|
568 | },
|
569 |
|
570 | _sampleOneIndex: function(cumulativeProbs) {
|
571 |
|
572 | var toTake = this.prng();
|
573 |
|
574 | // Find out where this lands in weights
|
575 | var cur = 0;
|
576 | while(toTake > cumulativeProbs[cur]) cur++;
|
577 |
|
578 | return cur;
|
579 | },
|
580 |
|
581 |
|
582 | // Return default if undefined, otherwise validate
|
583 | _v: function(param, type, defaultParam) {
|
584 | if(param == null && defaultParam != null)
|
585 | return defaultParam;
|
586 |
|
587 | switch(type) {
|
588 |
|
589 | // Array of 1 item or more
|
590 | case "a":
|
591 | if(!Array.isArray(param) || !param.length) throw "Expected an array of length 1 or greater";
|
592 | return param;
|
593 |
|
594 | // Natural number
|
595 | case "n":
|
596 | if(param === undefined) throw "You must specify how many values you want";
|
597 | if(param !== Number(param)) throw "The number of values must be numeric";
|
598 | if(param !== Math.round(param)) throw "The number of values must be a whole number";
|
599 | if(param < 1) throw "The number of values must be a whole number of 1 or greater";
|
600 | if(param === Infinity) throw "The number of values cannot be infinite ;-)";
|
601 | return param;
|
602 |
|
603 | // Valid probability
|
604 | case "p":
|
605 | if(Number(param) !== param) throw "Probability value is missing or not a number";
|
606 | if(param > 1) throw "Probability values cannot be greater than 1";
|
607 | if(param < 0) throw "Probability values cannot be less than 0";
|
608 | return param;
|
609 |
|
610 | // Positive numbers
|
611 | case "pos":
|
612 | if(Number(param) !== param) throw "A required parameter is missing or not a number";
|
613 | if(param <= 0) throw "Parameter must be greater than 0";
|
614 | if(param === Infinity) throw 'Sent "infinity" as a parameter';
|
615 | return param;
|
616 |
|
617 | // Look for numbers (reals)
|
618 | case "r":
|
619 | if(Number(param) !== param) throw "A required parameter is missing or not a number";
|
620 | if(param === Infinity) throw 'Sent "infinity" as a parameter';
|
621 | return param;
|
622 |
|
623 | // Non negative real number
|
624 | case "nn":
|
625 | if(param !== Number(param)) throw "A required parameter is missing or not a number";
|
626 | if(param < 0) throw "Parameter cannot be less than 0";
|
627 | if(param === Infinity) throw 'Sent "infinity" as a parameter';
|
628 | return param;
|
629 |
|
630 | // Non negative whole number (integer)
|
631 | case "nni":
|
632 | if(param !== Number(param)) throw "A required parameter is missing or not a number";
|
633 | if(param !== Math.round(param)) throw "Parameter must be a whole number";
|
634 | if(param < 0) throw "Parameter cannot be less than zero";
|
635 | if(param === Infinity) throw 'Sent "infinity" as a parameter';
|
636 | return param;
|
637 |
|
638 | }
|
639 | },
|
640 |
|
641 |
|
642 | // ________ _______ ______ _____ _____ __ __ ______ _ _ _______ _
|
643 | // | ____\ \ / / __ \| ____| __ \|_ _| \/ | ____| \ | |__ __|/\ | |
|
644 | // | |__ \ V /| |__) | |__ | |__) | | | | \ / | |__ | \| | | | / \ | |
|
645 | // | __| > < | ___/| __| | _ / | | | |\/| | __| | . ` | | | / /\ \ | |
|
646 | // | |____ / . \| | | |____| | \ \ _| |_| | | | |____| |\ | | |/ ____ \| |____
|
647 | // |______/_/ \_\_| |______|_| \_\_____|_| |_|______|_| \_| |_/_/ \_\______|
|
648 |
|
649 | /**
|
650 | *
|
651 | * @param n Number of variates to return
|
652 | * @param loc Starting point. Must be a non-negative integer. 0 for degenerate distribution of 0.
|
653 | * @param p Probability of moving towards finish
|
654 | * @param cap Maximum steps before giving up
|
655 | * @param trace Variable to track progress
|
656 | * @returns {Array} Random variates array
|
657 | *
|
658 | * The FML distribution is a is based on the number of steps taken to return to the origin
|
659 | * from a given position, with transition probabilities set at the beginning by picking a
|
660 | * random variate from U(0,1).
|
661 | */
|
662 | rfml: function (n, loc, p, cap, trace) {
|
663 | n = this._v(n, "n");
|
664 | loc = this._v(loc, "nni", 1);
|
665 | if(p === undefined) p=this.prng;
|
666 | cap = this._v(cap, "n", 10000);
|
667 | if(trace === undefined) trace={};
|
668 |
|
669 | var toReturn = [];
|
670 |
|
671 | for(var i=0; i<n; i++) {
|
672 | var x = 0;
|
673 | var s = loc;
|
674 | var currP = p();
|
675 | if(loc === 0) {
|
676 |
|
677 | toReturn[i] = 0;
|
678 | } else {
|
679 |
|
680 | do {
|
681 |
|
682 | var trial = this.prng();
|
683 | if(trial < currP) {
|
684 | s++;
|
685 | trace[String(i) + "_" + String(x)] = { problems: s, p: currP, result: 1 }
|
686 | } else {
|
687 | s--;
|
688 | trace[String(i) + "_" + String(x)] = { problems: s, p: currP, result: -1 }
|
689 | }
|
690 | x++
|
691 | } while(s > 0 && x < cap);
|
692 |
|
693 | if(x === cap) x = -1; // Indicate we failed to do it in time.
|
694 | toReturn[i] = x;
|
695 | }
|
696 | }
|
697 | return toReturn
|
698 | },
|
699 |
|
700 | // http://www.statisticsblog.com/2013/05/uncovering-the-unreliable-friend-distribution-a-case-study-in-the-limits-of-mc-methods/
|
701 | /**
|
702 | *
|
703 | * The Unrelaible Friend distribution
|
704 | * @param n
|
705 | * @returns {Array} Random variates array
|
706 | */
|
707 | ruf: function(n) {
|
708 | n = this._v(n, "n");
|
709 |
|
710 | var toReturn = [];
|
711 |
|
712 | for(var i=0; i<n; i++) {
|
713 | toReturn[i] = this.rexp(1, this.prng())[0];
|
714 | }
|
715 |
|
716 | return toReturn
|
717 | }
|
718 | };
|
719 |
|
720 | // TODO: Add "perfect fake" functions: http://www.statisticsblog.com/2010/06/the-perfect-fake/
|
721 | // NOTES
|
722 | // Potential config options:
|
723 | // default entropy amount
|
724 | // Need pathway to make ready for secure applications (NIST/diehard?)
|
725 | // Always return a vector unless number is 1? This could be config option or put "1" at end of fcn to get 1 only
|
726 | // Separate out core random variate creation from number to create loop
|
727 | // TODO: To test out quality of randomness, stub in specific values for this.prng and make sure correct stuff is returned. |
\ | No newline at end of file |