Skip to content

Instantly share code, notes, and snippets.

@vjeranc
Last active October 21, 2023 16:20
Show Gist options
  • Save vjeranc/d01aa7a4ccbc2331e00cfcadc310de61 to your computer and use it in GitHub Desktop.
Save vjeranc/d01aa7a4ccbc2331e00cfcadc310de61 to your computer and use it in GitHub Desktop.
A JavaScript module implementing the Alea pseudo-random number generator with seed, offering functions to generate float32, uint32, fract53, and Gaussian-distributed random numbers for non-cryptographic purposes. Original author: Baagøe https://github.com/nquinlan/better-random-numbers-for-javascript-mirror
interface AleaPRNG {
float32: () => number;
uint32: () => number;
fract53: () => number;
gaussian: (mean?: number, std?: number) => number;
}
/**
* Alea PRNG that is not cryptographically secure.
*/
export const mkAlea = (seed: string): AleaPRNG => {
const randomFloat32 = (() => {
let n = 0xefc8249d;
const mash = (data: string) => {
for (let i = 0; i < data.length; i++) {
n += data.charCodeAt(i);
let h = 0.02519603282416938 * n;
n = h >>> 0;
h -= n;
h *= n;
n = h >>> 0;
h -= n;
n += h * 0x100000000; // 2^32
}
return (n >>> 0) * 2.3283064365386963e-10; // 2^-32
};
// keeping the order of mash calls the same
let s0 = mash(" ");
let s1 = mash(" ");
let s2 = mash(" ");
let c = 1;
s0 -= mash(seed);
if (s0 < 0) {
s0 += 1;
}
s1 -= mash(seed);
if (s1 < 0) {
s1 += 1;
}
s2 -= mash(seed);
if (s2 < 0) {
s2 += 1;
}
// state setup is done, now make randomFloat32()
return () => {
const t = 2091639 * s0 + c * 2.3283064365386963e-10; // 2^-32
s0 = s1;
s1 = s2;
return (s2 = t - (c = t | 0));
};
})();
const randomUint32 = () => {
return randomFloat32() * 0x100000000; // 2^32
};
const randomFract53 = () => {
return (
randomFloat32() +
((randomFloat32() * 0x200000) | 0) * 1.1102230246251565e-16
); // 2^-53
};
const randomGaussian = (() => {
// plotted 100k numbers, looks good enough on a histogram chart
let spare: number | undefined;
return (mean = 0, std = 1) => {
if (spare != null) {
const r = spare * std + mean;
spare = undefined;
return r;
}
let u: number;
let v: number;
let s: number;
do {
u = randomFloat32() * 2 - 1;
v = randomFloat32() * 2 - 1;
s = u * u + v * v;
} while (s >= 1 || s === 0);
s = Math.sqrt((-2 * Math.log(s)) / s);
spare = v * s;
return mean + std * u * s;
};
})();
return {
float32: randomFloat32,
uint32: randomUint32,
fract53: randomFract53,
gaussian: randomGaussian,
};
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment