mysticsymbolic.github.io/lib/random.ts

118 wiersze
2.9 KiB
TypeScript

import {
inclusiveRange,
NumericInterval,
NumericRange,
GaussianDist,
} from "./util";
export type RandomParameters = {
modulus: number;
multiplier: number;
increment: number;
};
const NUMERICAL_RECIPES_PARAMETERS: RandomParameters = {
modulus: Math.pow(2, 32),
multiplier: 1664525,
increment: 1013904223,
};
/**
* A simple linear congruential random number generator, as described in
* https://en.wikipedia.org/wiki/Linear_congruential_generator.
*/
export class Random {
private latestSeed: number;
constructor(
readonly seed: number = Date.now(),
readonly params: RandomParameters = NUMERICAL_RECIPES_PARAMETERS
) {
this.latestSeed = seed;
}
/**
* Create an exact replica of this instance.
*/
clone(): Random {
return new Random(this.latestSeed, this.params);
}
/**
* Return a random number that is greater than or equal to zero, and less
* than one.
*/
next(): number {
this.latestSeed =
(this.params.multiplier * this.latestSeed + this.params.increment) %
this.params.modulus;
return this.latestSeed / this.params.modulus;
}
/**
* Return a random boolean with the given probability of being true.
*/
bool(trueProbability: number = 0.5): boolean {
return this.next() < trueProbability;
}
/**
* Return a number in the given range, inclusive.
*/
inRange(range: NumericRange): number {
return this.choice(inclusiveRange(range));
}
/**
* Return a number in the interval, second argument is really supremum which return value is always less than
*/
inInterval({ min, max }: NumericInterval): number {
return this.next() * (max - min) + min;
}
/**
* Return a number from the specified gaussian distribution
* from: https://stackoverflow.com/questions/25582882/javascript-math-random-normal-distribution-gaussian-bell-curve
*/
fromGaussian({ mean, stddev }: GaussianDist, nsamples = 6) {
let runtotal = 0;
for (var i = 0; i < nsamples; i++) {
runtotal += this.next();
}
return (stddev * (runtotal - nsamples / 2)) / (nsamples / 2) + mean;
}
/**
* Return a random item from the given array. If the array is
* empty, an exception is thrown.
*/
choice<T>(array: T[]): T {
if (array.length === 0) {
throw new Error("Cannot choose randomly from an empty array!");
}
const idx = Math.floor(this.next() * array.length);
return array[idx];
}
/**
* Attempt to randomly choose *at most* the given number of unique items from
* the array. If the array is too small, fewer items are returned.
*/
uniqueChoices<T>(array: T[], howMany: number): T[] {
let choicesLeft = [...array];
const result: T[] = [];
for (let i = 0; i < howMany; i++) {
if (choicesLeft.length === 0) {
break;
}
const choice = this.choice(choicesLeft);
choicesLeft = choicesLeft.filter((item) => item !== choice);
result.push(choice);
}
return result;
}
}