sample2.js (3426B)
1 /** 2 * @license Apache-2.0 3 * 4 * Copyright (c) 2018 The Stdlib Authors. 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 'use strict'; 20 21 // MODULES // 22 23 var floor = require( '@stdlib/math/base/special/floor' ); 24 var sign = require( '@stdlib/math/base/special/signum' ); 25 var sqrt = require( '@stdlib/math/base/special/sqrt' ); 26 var abs = require( '@stdlib/math/base/special/abs' ); 27 var ln = require( '@stdlib/math/base/special/ln' ); 28 var correction = require( './correction.js' ); 29 30 31 // VARIABLES // 32 33 var ONE_SIXTH = 1.0 / 6.0; 34 35 36 // MAIN // 37 38 /** 39 * Generates a binomially distributed pseudorandom number. 40 * 41 * ## References 42 * 43 * - Hörmann, Wolfgang. 1993. "The generation of binomial random variates." _Journal of Statistical Computation and Simulation_ 46 (1-2): 101–10. doi:[10.1080/00949659308811496][@hormann:1993a]. 44 * 45 * [@hormann:1993a]: http://dx.doi.org/10.1080/00949659308811496 46 * 47 * @private 48 * @param {PRNG} rand - PRNG for uniformly distributed numbers 49 * @param {PositiveInteger} n - number of trials 50 * @param {Probability} p - success probability 51 * @returns {NonNegativeInteger} pseudorandom number 52 */ 53 function sample( rand, n, p ) { 54 var alpha; 55 var urvr; 56 var snpq; 57 var npq; 58 var rho; 59 var tmp; 60 var nm; 61 var nr; 62 var us; 63 var km; 64 var nk; 65 var vr; 66 var a; 67 var b; 68 var c; 69 var f; 70 var h; 71 var i; 72 var k; 73 var m; 74 var q; 75 var r; 76 var t; 77 var u; 78 var v; 79 var x; 80 81 m = floor( (n + 1) * p ); 82 nm = n - m + 1; 83 84 q = 1.0 - p; 85 86 r = p / q; 87 nr = (n + 1) * r; 88 89 npq = n * p * q; 90 snpq = sqrt( npq ); 91 92 b = 1.15 + (2.53 * snpq); 93 a = -0.0873 + (0.0248*b) + (0.01*p); 94 c = (n*p) + 0.5; 95 96 alpha = (2.83 + (5.1/b)) * snpq; 97 98 vr = 0.92 - (4.2/b); 99 urvr = 0.86 * vr; 100 101 h = (m + 0.5) * ln( (m+1) / (r*nm) ); 102 h += correction( m ) + correction( n-m ); 103 104 while ( true ) { 105 v = rand(); 106 if ( v <= urvr ) { 107 u = (v/vr) - 0.43; 108 r = (u * ( (2.0*a / (0.5 - abs(u))) + b )) + c; 109 return floor( r ); 110 } 111 if ( v >= vr ) { 112 u = rand() - 0.5; 113 } else { 114 u = (v/vr) - 0.93; 115 u = (sign( u ) * 0.5) - u; 116 v = vr * rand(); 117 } 118 us = 0.5 - abs(u); 119 k = floor( (u * ( (2.0*a/us) + b )) + c ); 120 if ( k < 0 || k > n ) { 121 // Try again... 122 continue; 123 } 124 v = v * alpha / ( (a/(us*us)) + b ); 125 km = abs( k - m ); 126 if ( km > 15 ) { 127 v = ln( v ); 128 rho = km / npq; 129 tmp = ( (km/3) + 0.625 ) * km; 130 tmp += ONE_SIXTH; 131 tmp /= npq; 132 rho *= tmp + 0.5; 133 t = -(km * km) / (2.0 * npq); 134 if ( v < t - rho ) { 135 return k; 136 } 137 if ( v <= t + rho ) { 138 nk = n - k + 1; 139 x = h + ( (n+1)*ln( nm/nk ) ); 140 x += (k+0.5) * ln( nk*r/(k+1) ); 141 x += -(correction( k ) + correction( n-k )); 142 if ( v <= x ) { 143 return k; 144 } 145 } 146 } else { 147 f = 1.0; 148 if ( m < k ) { 149 for ( i = m; i <= k; i++ ) { 150 f *= (nr/i) - r; 151 } 152 } else if ( m > k ) { 153 for ( i = k; i <= m; i++ ) { 154 v *= (nr/i) - r; 155 } 156 } 157 if ( v <= f ) { 158 return k; 159 } 160 } 161 } 162 } 163 164 165 // EXPORTS // 166 167 module.exports = sample;