Skip to content

Distributions

ransu.normal(170, 6); // 168.42…
ransu.poisson(4); // 3
ransu.binomial(100, 0.3); // 28
ransu.exponential(2);
ransu.zipf(1.2, 1000);

Same name, two shapes: positional arguments give a number, an options object gives a sampler. The samplers live in ransu/distribution.

import { normal, categorical } from "ransu/distribution";
const height = normal({ mean: 170, sd: 6 });
height.sample(); // one draw
height.samples(10_000); // a Float64Array
height.mean; // 170
height.variance; // 36
const loot = categorical({ weights: [70, 25, 5] }); // O(1) per draw

Build the sampler when you draw more than once: it validates its parameters and precomputes its constants up front.

Continuousuniform, normal, logNormal, exponential, gamma, beta, chiSquared, studentT, fisherF, cauchy, laplace, logistic, gumbel, pareto, weibull, rayleigh, triangular, irwinHall, bates, vonMises, dirichlet.

Discretebernoulli, discreteUniform, categorical, binomial, poisson, geometric, negativeBinomial, hypergeometric, zipf, multinomial.

Distribution Method
normal 128-level ziggurat, tables built numerically at load
gamma Marsaglia–Tsang, with a boost for shapes below 1
binomial CDF inversion when n·p < 10, Hörmann’s BTRS above
poisson Knuth when λ < 30, Hörmann’s PTRS above
categorical Vose’s alias method
hypergeometric Sequential urn sampling — exact
Everything else Inverse transform, or composition of the above

The rejection methods compare against the exact log-pmf rather than a Stirling approximation. The rejection rate is low enough that the extra cost does not matter, and it removes any chance of a mis-transcribed constant.

Matching the mean does not prove a distribution is right. CI runs:

  • Kolmogorov–Smirnov against the exact CDF for the continuous families.
  • Chi-square against the exact pmf for the discrete ones — separately for each algorithm branch, so both the inversion and the rejection paths are covered.
  • For the normal, the 3σ and 4σ tail rates, plus skewness and kurtosis. The ziggurat only enters its tail routine beyond 3.44σ, so a moment test alone would never notice a broken tail.
const r = new Random(42);
normal({ mean: 0, sd: 1, engine: r.engine }).sample();

Without engine, a sampler draws from the global stream, so seed() makes it reproducible too.