improved_ziggurat.js (2609B)
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 abs = require( '@stdlib/math/base/special/abs' ); 24 var exp = require( '@stdlib/math/base/special/exp' ); 25 var coordsArray = require( './coords_array.js' ); 26 var ratioArray = require( './ratio_array.js' ); 27 var sampleTail = require( './sample_tail.js' ); 28 29 30 // VARIABLES // 31 32 // Number of blocks: 33 var NUM_BLOCKS = 128; 34 35 // Start of right tail (R): 36 var START_RIGHT_TAIL = 3.442619855899; 37 38 // `X` holds coordinates, such that each rectangle has same area: 39 var X = coordsArray( NUM_BLOCKS, START_RIGHT_TAIL ); 40 41 // `R` holds `X[ i+1 ] / X[ i ]`: 42 var R = ratioArray( X ); 43 44 // 127 => 0x7F => 00000000000000000000000001111111 45 var LAST_7_BITS_MASK = 127|0; // asm type annotation 46 47 48 // MAIN // 49 50 /** 51 * Returns a pseudorandom number generator which implements the improved Ziggurat algorithm for generating normally distributed pseudorandom numbers. 52 * 53 * @private 54 * @param {PRNG} randu - PRNG for generating uniformly distributed numbers 55 * @param {PRNG} randi - PRNG for generating uniformly distributed integers 56 * @returns {number} pseudorandom number 57 */ 58 function wrap( randu, randi ) { 59 return randn; 60 61 /** 62 * Generates a normally distributed pseudorandom number. 63 * 64 * @private 65 * @returns {number} pseudorandom number 66 * 67 * @example 68 * var r = randn(); 69 * // returns <number> 70 */ 71 function randn() { 72 var f0; 73 var f1; 74 var x2; 75 var x; 76 var u; 77 var i; 78 var j; 79 while ( true ) { 80 u = ( 2.0*randu() ) - 1.0; 81 i = randi() & LAST_7_BITS_MASK; 82 83 // First try the rectangular boxes... 84 if ( abs( u ) < R[ i ] ) { 85 return u * X[ i ]; 86 } 87 // If bottom box, sample from the tail... 88 if ( i === 0 ) { 89 return sampleTail( randu, START_RIGHT_TAIL, u < 0.0 ); 90 } 91 // Is this a sample from the wedges? 92 x = u * X[ i ]; 93 x2 = x * x; 94 j = i + 1; 95 f0 = exp( -0.5 * ( (X[ i ]*X[ i ]) - x2 ) ); 96 f1 = exp( -0.5 * ( (X[ j ]*X[ j ]) - x2 ) ); 97 if ( f1 + (randu()*(f0-f1)) < 1.0 ) { 98 return x; 99 } 100 } 101 } 102 } 103 104 105 // EXPORTS // 106 107 module.exports = wrap;