Last active
October 21, 2023 16:20
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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