UNPKG

mathjs

Version:

Math.js is an extensive math library for JavaScript and Node.js. It features a flexible expression parser with support for symbolic computation, comes with a large set of built-in functions and constants, and offers an integrated solution to work with dif

142 lines (117 loc) 4.45 kB
import { factory } from '../../utils/factory' import { isNumber } from '../../utils/is' import { arraySize } from '../../utils/array' import { createRng } from './util/seededRNG' const name = 'pickRandom' const dependencies = ['typed', 'config', '?on'] export const createPickRandom = /* #__PURE__ */ factory(name, dependencies, ({ typed, config, on }) => { // seeded pseudo random number generator let rng = createRng(config.randomSeed) if (on) { on('config', function (curr, prev) { if (curr.randomSeed !== prev.randomSeed) { rng = createRng(curr.randomSeed) } }) } /** * Random pick one or more values from a one dimensional array. * Array elements are picked using a random function with uniform or weighted distribution. * * Syntax: * * math.pickRandom(array) * math.pickRandom(array, number) * math.pickRandom(array, weights) * math.pickRandom(array, number, weights) * math.pickRandom(array, weights, number) * * Examples: * * math.pickRandom([3, 6, 12, 2]) // returns one of the values in the array * math.pickRandom([3, 6, 12, 2], 2) // returns an array of two of the values in the array * math.pickRandom([3, 6, 12, 2], [1, 3, 2, 1]) // returns one of the values in the array with weighted distribution * math.pickRandom([3, 6, 12, 2], 2, [1, 3, 2, 1]) // returns an array of two of the values in the array with weighted distribution * math.pickRandom([3, 6, 12, 2], [1, 3, 2, 1], 2) // returns an array of two of the values in the array with weighted distribution * * See also: * * random, randomInt * * @param {Array | Matrix} array A one dimensional array * @param {Int} number An int or float * @param {Array | Matrix} weights An array of ints or floats * @return {number | Array} Returns a single random value from array when number is 1 or undefined. * Returns an array with the configured number of elements when number is > 1. */ return typed({ 'Array | Matrix': function (possibles) { return _pickRandom(possibles) }, 'Array | Matrix, number': function (possibles, number) { return _pickRandom(possibles, number, undefined) }, 'Array | Matrix, Array': function (possibles, weights) { return _pickRandom(possibles, undefined, weights) }, 'Array | Matrix, Array | Matrix, number': function (possibles, weights, number) { return _pickRandom(possibles, number, weights) }, 'Array | Matrix, number, Array | Matrix': function (possibles, number, weights) { return _pickRandom(possibles, number, weights) } }) function _pickRandom (possibles, number, weights) { const single = (typeof number === 'undefined') if (single) { number = 1 } possibles = possibles.valueOf() // get Array if (weights) { weights = weights.valueOf() // get Array } if (arraySize(possibles).length > 1) { throw new Error('Only one dimensional vectors supported') } let totalWeights = 0 if (typeof weights !== 'undefined') { if (weights.length !== possibles.length) { throw new Error('Weights must have the same length as possibles') } for (let i = 0, len = weights.length; i < len; i++) { if (!isNumber(weights[i]) || weights[i] < 0) { throw new Error('Weights must be an array of positive numbers') } totalWeights += weights[i] } } const length = possibles.length if (length === 0) { return [] } else if (number >= length) { return number > 1 ? possibles : possibles[0] } const result = [] let pick while (result.length < number) { if (typeof weights === 'undefined') { pick = possibles[Math.floor(rng() * length)] } else { let randKey = rng() * totalWeights for (let i = 0, len = possibles.length; i < len; i++) { randKey -= weights[i] if (randKey < 0) { pick = possibles[i] break } } } if (result.indexOf(pick) === -1) { result.push(pick) } } return single ? result[0] : result // TODO: return matrix when input was a matrix // TODO: add support for multi dimensional matrices } })