diff --git a/src/js/random.js b/src/js/random.js new file mode 100644 index 0000000000000000000000000000000000000000..1f7f6c4812f4c9c3119d8d9ea885320bc64f2189 --- /dev/null +++ b/src/js/random.js @@ -0,0 +1,75 @@ +/** + * generate two independent Gaussian numbers using Box-Muller transform. + * mean and deviation specify the desired mean and standard deviation. + * @returns {number[]} + */ +window.gaussianPair = function(mean = 0, deviation = 1) { + const r = Math.sqrt(-2.0 * Math.log(1 - Math.random())); + const sigma = 2.0 * Math.PI * (1 - Math.random()); + return [r * Math.cos(sigma), r * Math.sin(sigma)].map(val => val * deviation + mean); +}; + +// Generate a random integer with a normal distribution between min and max (both inclusive). +// Default parameters result in truncating the standard normal distribution between -3 and +3. +// Not specifying min/max results in rerolling val approximately 0.3% of the time. +window.normalRandInt = function(mean = 0, deviation = 1, min = mean - 3 * deviation, max = mean + 3 * deviation) { + let val = gaussianPair(mean, deviation)[0]; + while (val < min || val > max) { + val = gaussianPair(mean, deviation)[0]; + } + return Math.round(val); +} + +/** + * Returns a random integer between min and max (both inclusive). + * If count is defined, chooses that many random numbers between min and max and returns the average. This is an approximation of a normal distribution. + * @param {number} min + * @param {number} max + * @returns {number} + */ +window.jsRandom = function(min, max, count = 1) { + function rand() { + return Math.random() * (max - min + 1) + min; + } + + if (count === 1) { + return Math.floor(rand()); + } + + let total = 0; + for (let i = 0; i < count; i++) { + total += rand(); + } + return Math.floor(total/count); +}; + +/** + * Chooses multiple random elements of an array. + * @param {number[]} arr + * @param {number} count + * @returns {number[]} + */ +window.jsRandomMany = function(arr, count) { + let result = []; + let _tmp = arr.slice(); + for (let i = 0; i < count; i++) { + let index = Math.floor(Math.random() * _tmp.length); + result.push(_tmp.splice(index, 1)[0]); + } + return result; +}; + +/** + * Accepts both an array and a list, returns undefined if nothing is passed. + * @param {number|string|number[]|string[]} choices + * @param {...(number|string|number[]|string[])} otherChoices + * @returns {number|string|number[]|string[]|undefined} + */ +window.jsEither = function(choices, ...otherChoices) { + if (otherChoices.length === 0 && Array.isArray(choices)) { + return choices[Math.floor(Math.random() * choices.length)]; + } + const allChoices = otherChoices; + allChoices.push(choices); + return allChoices[Math.floor(Math.random() * allChoices.length)]; +}; diff --git a/src/js/utilJS.js b/src/js/utilJS.js index e3c55587afe30d6d4d093d1c7c64e6387d4008cc..8dc1e4ab0ad6f3f5064405069fce3dfe5a9ee726 100644 --- a/src/js/utilJS.js +++ b/src/js/utilJS.js @@ -881,19 +881,6 @@ window.Intelligence = (function() { }; })(); -/** - * Helper method - generate two independent Gaussian numbers using Box-Muller transform - * @returns {number[]} - */ -window.gaussianPair = function() { - let r = Math.sqrt(-2.0 * Math.log(1 - Math.random())); - let sigma = 2.0 * Math.PI * (1 - Math.random()); - return [r * Math.cos(sigma), r * Math.sin(sigma)]; -}; - -// generate a random integer with a normal distribution -window.normalRandInt = (mean, scale) => Math.floor(mean + gaussianPair()[0] * scale); - /* A categorizer is used to "slice" a value range into distinct categories in an efficient manner. @@ -1289,46 +1276,6 @@ window.isFloat = function(n) { return Number.isFinite(n) && Math.floor(n) !== n; }; -/** - * Returns a random number between min and max - * @param {number} min - * @param {number} max - * @returns {number} - */ -window.jsRandom = function(min, max) { - return Math.floor(Math.random() * (max - min + 1) + min); -}; - -/** - * @param {number[]} arr - * @param {number} count - * @returns {number[]} - */ -window.jsRandomMany = function(arr, count) { - let result = []; - let _tmp = arr.slice(); - for (let i = 0; i < count; i++) { - let index = Math.ceil(Math.random() * 10) % _tmp.length; - result.push(_tmp.splice(index, 1)[0]); - } - return result; -}; - -/** - * Accepts both an array and a list, returns undefined if nothing is passed. - * @param {number|string|number[]|string[]} choices - * @param {...(number|string|number[]|string[])} otherChoices - * @returns {number|string|number[]|string[]|undefined} - */ -window.jsEither = function(choices, ...otherChoices) { - if (otherChoices.length === 0 && Array.isArray(choices)) { - return choices[Math.floor(Math.random() * choices.length)]; - } - const allChoices = otherChoices; - allChoices.push(choices); - return allChoices[Math.floor(Math.random() * allChoices.length)]; -}; - /* Make everything waiting for this execute. Usage: