import prand from 'pure-rand' export default class RandomGenerator { private generator: prand.RandomGenerator public seed: number constructor(seed?: number) { this.seed = seed ?? Date.now() ^ (Math.random() * 0x100000000) this.generator = prand.xoroshiro128plus(seed) } public jump(): void { this.generator = this.generator.jump() } /** * Returns a uniformly distributed random integer between min and max */ public uniformInt(min: number, max: number): number { const [value, rng] = prand.uniformIntDistribution(min, max)(this.generator) this.generator = rng return value } /** * Returns an array of uniformly distributed random integers between min and max */ public uniformIntArray(min: number, max: number, size: number): number[] { const result: number[] = [] for (let i = 0; i < size; i++) { result.push(this.uniformInt(min, max)) } return result } /* * Returns a uniformly distributed random float between min and max */ public uniformFloat(min: number, max: number) { const [value, rng] = prand.uniformIntDistribution(min * 1000, max * 1000)(this.generator) this.generator = rng return value / 1000 } /** * Returns an array of uniformly distributed random floats between min and max. */ public uniformFloatArray(min: number, max: number, size: number): number[] { const result: number[] = [] for (let i = 0; i < size; i++) { result.push(this.uniformFloat(min, max)) } return result } /** * Returns a normally distributed random number with mean mu and standard deviation sigma. */ public normalFloat(mu: number, sigma: number): number { let u = 0, v = 0 while(u === 0) u = 1 - this.uniformFloat(0, 1) while(v === 0) v = this.uniformFloat(0, 1) return sigma * Math.sqrt( -2.0 * Math.log( u ) ) * Math.cos( 2.0 * Math.PI * v ) + mu } /** * Returns an array of normally distributed random numbers with mean mu and standard deviation sigma. */ public normalFloatArray(mu: number, sigma: number, size: number): number[] { const result: number[] = [] for (let i = 0; i < size; i++) { result.push(this.normalFloat(mu, sigma)) } return result } /** * Returns a Poisson distributed random number with mean lambda. */ public poissonInt(lambda: number): number { const L = Math.exp(-lambda) let k = 0 let p = 1 do { k++ p *= this.uniformFloat(0, 1) } while (p > L) return k - 1 } /** * Returns an array of Poisson distributed random numbers with mean lambda. */ public poissonIntArray(lambda: number, size: number): number[] { const result: number[] = [] for (let i = 0; i < size; i++) { result.push(this.poissonInt(lambda)) } return result } /** * Picks a single random element from the array. * Returns undefined if the array is empty. */ public pickRandom(array: T[]): T { if (array.length === 0) { return undefined } return array[this.uniformInt(0, array.length - 1)] } /** * Picks a random subset of the array without replacement. * Returns an empty array if the array is empty. */ public pickRandomArray(array: T[], size: number): T[] { if (array.length === 0) { return [] } const result: T[] = [] const copy = array.slice() for (let i = 0; i < size; i++) { const index = this.uniformInt(0, copy.length - 1) result.push(copy[index]) copy.splice(index, 1) } return result } public shuffle(array: T[]): T[] { const result = array.slice() for (let i = result.length - 1; i > 0; i--) { const j = this.uniformInt(0, i) const temp = result[i] result[i] = result[j] result[j] = temp } return result } }