Initial
This commit is contained in:
70
src/rng.js
Normal file
70
src/rng.js
Normal file
@@ -0,0 +1,70 @@
|
||||
/* ============================================================
|
||||
rng.js — deterministic seeding & sampling
|
||||
Every stochastic decision in the generator draws from a
|
||||
salted sub-stream so that changing one parameter does not
|
||||
reshuffle unrelated parts of the scene.
|
||||
============================================================ */
|
||||
|
||||
/* cyrb53 — fast 53-bit string hash. Stable across runs/machines. */
|
||||
export function cyrb53(str, seed = 0) {
|
||||
let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed;
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
const ch = str.charCodeAt(i);
|
||||
h1 = Math.imul(h1 ^ ch, 2654435761);
|
||||
h2 = Math.imul(h2 ^ ch, 1597334677);
|
||||
}
|
||||
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^ Math.imul(h2 ^ (h2 >>> 13), 3266489909);
|
||||
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909);
|
||||
return (4294967296 * (2097151 & h2) + (h1 >>> 0)).toString(16).padStart(13, '0');
|
||||
}
|
||||
|
||||
/* mulberry32 — tiny fast PRNG, returns [0,1). */
|
||||
export function mulberry32(seed) {
|
||||
let a = seed >>> 0;
|
||||
return function () {
|
||||
a = (a + 0x6D2B79F5) >>> 0;
|
||||
let t = a;
|
||||
t = Math.imul(t ^ (t >>> 15), t | 1);
|
||||
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
|
||||
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
||||
};
|
||||
}
|
||||
|
||||
/* makeRng — seed string + salt → independent PRNG stream. */
|
||||
export function makeRng(seedStr, salt = '') {
|
||||
const h = cyrb53(seedStr + '::' + salt);
|
||||
return mulberry32(parseInt(h.slice(0, 8), 16));
|
||||
}
|
||||
|
||||
/* ---------- Distributions ---------- */
|
||||
|
||||
/* Standard normal via Box-Muller. */
|
||||
export function gauss(rng) {
|
||||
const u = 1 - rng(), v = rng();
|
||||
return Math.sqrt(-2 * Math.log(u)) * Math.cos(2 * Math.PI * v);
|
||||
}
|
||||
|
||||
/* Log-normal — heavy-tailed positive sample. */
|
||||
export function logNormal(rng, mu, sigma) {
|
||||
return Math.exp(mu + sigma * gauss(rng));
|
||||
}
|
||||
|
||||
/* Uniform in [lo, hi). */
|
||||
export function range(rng, lo, hi) {
|
||||
return lo + (hi - lo) * rng();
|
||||
}
|
||||
|
||||
/* Random int in [lo, hi]. */
|
||||
export function randInt(rng, lo, hi) {
|
||||
return lo + Math.floor(rng() * (hi - lo + 1));
|
||||
}
|
||||
|
||||
/* Pick one element. */
|
||||
export function pick(rng, arr) {
|
||||
return arr[Math.floor(rng() * arr.length)];
|
||||
}
|
||||
|
||||
/* Bernoulli. */
|
||||
export function chance(rng, p) {
|
||||
return rng() < p;
|
||||
}
|
||||
Reference in New Issue
Block a user