rng.js

/**
 * @namespace rng
 */

/**
 * Random Integer generator based on Math.random()
 * inclusive min, inclusive max
 *
 * @example
 * console.log(rng.generateRandomInteger(0, 5));    // 5
 * console.log(rng.generateRandomInteger(0, 5));    // 4
 * console.log(rng.generateRandomInteger(0, 5));    // 4
 * console.log(rng.generateRandomInteger(0, 5));    // 0
 * console.log(rng.generateRandomInteger(0, 5));    // 5
 *
 * console.log(rng.generateRandomInteger(0, 100));  // 45
 * console.log(rng.generateRandomInteger(0, 100));  // 52
 * console.log(rng.generateRandomInteger(0, 100));  // 45
 * console.log(rng.generateRandomInteger(90, 100)); // 94
 * console.log(rng.generateRandomInteger(90, 100)); // 99
 *
 * @memberof rng
 * @param {number}  min
 * @param {number}  max
 * @returns {number}        Integer of range min, max
 */
export function generateRandomInteger(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
}

/**
 * Random Float generator based on Math.random()
 * inclusive min, exclusive max
 *
 * @example
 * console.log(rng.generateRandomFloat(0, 0.001));      // 0.00026509881625332786
 * console.log(rng.generateRandomFloat(0, 0.001));      // 0.00007506891359581824
 * console.log(rng.generateRandomFloat(0, 0.001));      // 0.00044325961245963644
 * console.log(rng.generateRandomFloat(0, 0.001));      // 0.0009738804824279097
 * console.log(rng.generateRandomFloat(0, 0.001));      // 0.00037007902123962523
 *
 * console.log(rng.generateRandomFloat(0, 1));          // 0.5439056343074258
 * console.log(rng.generateRandomFloat(0, 1));          // 0.7389564454471567
 * console.log(rng.generateRandomFloat(0, 1));          // 0.8800660182555697
 * console.log(rng.generateRandomFloat(0, 1));          // 0.028073711000210277
 * console.log(rng.generateRandomFloat(0, 1));          // 0.45954615798738296
 *
 * console.log(rng.generateRandomFloat(1, 2));          // 1.8467985822932083
 * console.log(rng.generateRandomFloat(2, 3));          // 2.603690406049185
 * console.log(rng.generateRandomFloat(3, 30));         // 14.428615314773008
 * console.log(rng.generateRandomFloat(50, 100));       // 78.90222904220667
 * console.log(rng.generateRandomFloat(1000, 10000));   // 8798.862959284734
 *
 * @memberof rng
 * @param {number}  min
 * @param {number}  max
 * @returns {number}        Float of range min, max
 */
export function generateRandomFloat(min, max) {
    return parseFloat(Math.random() * (max - min) + min);
}

/**
 * Linear Congruential Generator
 *
 * Learn more
 *  - [wikipedia]{@link https://en.wikipedia.org/wiki/Linear_congruential_generator}
 *  - [rosettacode]{@link https://rosettacode.org/wiki/Linear_congruential_generator}
 *  - [ams]{@link https://www.ams.org/journals/mcom/1999-68-225/S0025-5718-99-00996-5/S0025-5718-99-00996-5.pdf}
 *
 * @example
 * import { rng } from 'mathform';
 *
 * // ZX81 parameters
 * // (see wikipedia or ams links provided above for more parameters)
 * let modulus = 2 ** 16 + 1;
 * let multiplier = 75;
 * let increment = 74;
 * let seed = Math.floor(Math.random() * 2 ** 16);
 *
 * // Create the LCG objet
 * let lcg = new rng.LCG(modulus, multiplier, increment, seed);
 *
 * // Usage
 * lcg.step() // returns a random number where 0 <= number <= 2 ** 16
 *
 * @class LCG
 * @memberof rng
 */
export class LCG {
    get modulus() {
        return this._m;
    }

    get multiplier() {
        return this._a;
    }

    get increment() {
        return this._c;
    }

    get seed() {
        return this._X;
    }

    /**
     * The generator is defined by the recurrence relation
     *  - X<sub>0</sub> = (aX<sub>n</sub> + c) mod m
     *
     * Were
     * - 0 < m
     * - 0 < a < m
     * - 0 <= c < m
     * - 0 <= X<sub>0</sub> < m
     *
     * @memberof! rng.LCG#
     * @param {integer} Modulus     - 0 < m
     * @param {integer} Multiplier  - 0 < a < m
     * @param {integer} Increment   - 0 <= c < m
     * @param {number}  Seed        - 0 <= X0 < m
     * @constructor
     */
    constructor(modulus, multiplier, increment, seed) {
        this._m = modulus;
        this._a = multiplier;
        this._c = increment;
        this._X = seed;
    }

    /**
     * Iterates the algorithm once and returns the new seed
     *
     * @memberof! rng.LCG#
     * @returns {integer}
     * @public
     */
    step() {
        this._iterate();
        return this.seed;
    }

    /**
     * Iterates the algorithm once
     *
     * @memberof! rng.LCG#
     * @private
     */
    _iterate() {
        this._X = (this.multiplier * this.seed + this.increment) % this.modulus;
    }
}