123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140 |
- module.exports = random;
- // TODO: Deprecate?
- module.exports.random = random,
- module.exports.randomIterator = randomIterator
- /**
- * Creates seeded PRNG with two methods:
- * next() and nextDouble()
- */
- function random(inputSeed) {
- var seed = typeof inputSeed === 'number' ? inputSeed : (+new Date());
- return new Generator(seed)
- }
- function Generator(seed) {
- this.seed = seed;
- }
- /**
- * Generates random integer number in the range from 0 (inclusive) to maxValue (exclusive)
- *
- * @param maxValue Number REQUIRED. Omitting this number will result in NaN values from PRNG.
- */
- Generator.prototype.next = next;
- /**
- * Generates random double number in the range from 0 (inclusive) to 1 (exclusive)
- * This function is the same as Math.random() (except that it could be seeded)
- */
- Generator.prototype.nextDouble = nextDouble;
- /**
- * Returns a random real number from uniform distribution in [0, 1)
- */
- Generator.prototype.uniform = nextDouble;
- /**
- * Returns a random real number from a Gaussian distribution
- * with 0 as a mean, and 1 as standard deviation u ~ N(0,1)
- */
- Generator.prototype.gaussian = gaussian;
- function gaussian() {
- // use the polar form of the Box-Muller transform
- // based on https://introcs.cs.princeton.edu/java/23recursion/StdRandom.java
- var r, x, y;
- do {
- x = this.nextDouble() * 2 - 1;
- y = this.nextDouble() * 2 - 1;
- r = x * x + y * y;
- } while (r >= 1 || r === 0);
- return x * Math.sqrt(-2 * Math.log(r)/r);
- }
- /**
- * See https://twitter.com/anvaka/status/1296182534150135808
- */
- Generator.prototype.levy = levy;
- function levy() {
- var beta = 3 / 2;
- var sigma = Math.pow(
- gamma( 1 + beta ) * Math.sin(Math.PI * beta / 2) /
- (gamma((1 + beta) / 2) * beta * Math.pow(2, (beta - 1) / 2)),
- 1/beta
- );
- return this.gaussian() * sigma / Math.pow(Math.abs(this.gaussian()), 1/beta);
- }
- // gamma function approximation
- function gamma(z) {
- return Math.sqrt(2 * Math.PI / z) * Math.pow((1 / Math.E) * (z + 1 / (12 * z - 1 / (10 * z))), z);
- }
- function nextDouble() {
- var seed = this.seed;
- // Robert Jenkins' 32 bit integer hash function.
- seed = ((seed + 0x7ed55d16) + (seed << 12)) & 0xffffffff;
- seed = ((seed ^ 0xc761c23c) ^ (seed >>> 19)) & 0xffffffff;
- seed = ((seed + 0x165667b1) + (seed << 5)) & 0xffffffff;
- seed = ((seed + 0xd3a2646c) ^ (seed << 9)) & 0xffffffff;
- seed = ((seed + 0xfd7046c5) + (seed << 3)) & 0xffffffff;
- seed = ((seed ^ 0xb55a4f09) ^ (seed >>> 16)) & 0xffffffff;
- this.seed = seed;
- return (seed & 0xfffffff) / 0x10000000;
- }
- function next(maxValue) {
- return Math.floor(this.nextDouble() * maxValue);
- }
- /*
- * Creates iterator over array, which returns items of array in random order
- * Time complexity is guaranteed to be O(n);
- */
- function randomIterator(array, customRandom) {
- var localRandom = customRandom || random();
- if (typeof localRandom.next !== 'function') {
- throw new Error('customRandom does not match expected API: next() function is missing');
- }
- return {
- forEach: forEach,
- /**
- * Shuffles array randomly, in place.
- */
- shuffle: shuffle
- };
- function shuffle() {
- var i, j, t;
- for (i = array.length - 1; i > 0; --i) {
- j = localRandom.next(i + 1); // i inclusive
- t = array[j];
- array[j] = array[i];
- array[i] = t;
- }
- return array;
- }
- function forEach(callback) {
- var i, j, t;
- for (i = array.length - 1; i > 0; --i) {
- j = localRandom.next(i + 1); // i inclusive
- t = array[j];
- array[j] = array[i];
- array[i] = t;
- callback(t);
- }
- if (array.length) {
- callback(array[0]);
- }
- }
- }
|