feat: add CLI random number generator supporting 6 distributions
This commit is contained in:
+88
@@ -0,0 +1,88 @@
|
||||
import { type Options } from "./types";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Individual distribution samplers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/** Box-Muller transform. Each call consumes 2 uniform randoms. */
|
||||
function normalRandom(mean: number, stddev: number): number {
|
||||
let u1 = Math.random();
|
||||
while (u1 === 0) u1 = Math.random(); // avoid log(0)
|
||||
const u2 = Math.random();
|
||||
return mean + stddev * Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
|
||||
}
|
||||
|
||||
/** Sum of Bernoulli trials. */
|
||||
function binomialRandom(n: number, p: number): number {
|
||||
let s = 0;
|
||||
for (let i = 0; i < n; i++) {
|
||||
if (Math.random() < p) s++;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/** Knuth's algorithm. */
|
||||
function poissonRandom(lambda: number): number {
|
||||
const L = Math.exp(-lambda);
|
||||
let k = 0;
|
||||
let p = 1;
|
||||
do {
|
||||
k++;
|
||||
p *= Math.random();
|
||||
} while (p > L);
|
||||
return k - 1;
|
||||
}
|
||||
|
||||
/** Inverse CDF. */
|
||||
function exponentialRandom(lambda: number): number {
|
||||
return -Math.log(Math.random()) / lambda;
|
||||
}
|
||||
|
||||
/** Urn model — simulate drawing without replacement. */
|
||||
function hypergeometricRandom(N: number, K: number, n: number): number {
|
||||
let s = 0;
|
||||
let remainingK = K;
|
||||
let remainingTotal = N;
|
||||
const draws = Math.min(n, N);
|
||||
for (let i = 0; i < draws; i++) {
|
||||
if (Math.random() < remainingK / remainingTotal) {
|
||||
s++;
|
||||
remainingK--;
|
||||
}
|
||||
remainingTotal--;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Dispatcher
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export function generate(opts: Options): number[] {
|
||||
const results: number[] = [];
|
||||
for (let i = 0; i < opts.count; i++) {
|
||||
switch (opts.dist) {
|
||||
case "uniform":
|
||||
results.push(Math.random() * (opts.max - opts.min) + opts.min);
|
||||
break;
|
||||
case "normal":
|
||||
results.push(normalRandom(opts.mean, opts.stddev));
|
||||
break;
|
||||
case "binomial":
|
||||
results.push(binomialRandom(opts.trials, opts.prob));
|
||||
break;
|
||||
case "poisson":
|
||||
results.push(poissonRandom(opts.lambda));
|
||||
break;
|
||||
case "exponential":
|
||||
results.push(exponentialRandom(opts.lambda));
|
||||
break;
|
||||
case "hypergeometric":
|
||||
results.push(
|
||||
hypergeometricRandom(opts.popSize, opts.successes, opts.draws),
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
Reference in New Issue
Block a user