Skip to content

Instantly share code, notes, and snippets.

@jabney
Last active May 9, 2022 15:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jabney/003ba1504c13735aad6da18f618c8691 to your computer and use it in GitHub Desktop.
Save jabney/003ba1504c13735aad6da18f618c8691 to your computer and use it in GitHub Desktop.
Seedable random numbers using a hash
"use strict";
const crypto = require("crypto");
/**
* @param {string|number} [seed]
* @param {number} [offset]
*/
const createRng = (seed = 0, offset = 0) => {
const numBytes = 6; // 48 bits
const maxInt = 2 ** 48;
let count = offset;
/**
* @param {number|string} seed
*/
const createHash = (seed) => {
const hash = crypto.createHash("sha256");
hash.update(seed.toString());
return hash;
};
/**
* @param {crypto.Hash} hash
*/
const getInt = (hash) => {
const bytes = hash.digest("hex").slice(-2 * numBytes);
return parseInt(bytes, 16);
};
const randomFloat = () => {
count = (count % Number.MAX_SAFE_INTEGER) + 1;
const hash = createHash(seed);
hash.update(count.toString());
return getInt(hash) / maxInt;
};
/**
* @param {number} min inclusive
* @param {number} max exclusive
*/
const randomInt = (min = 0, max = maxInt) => {
const f = randomFloat();
return Math.floor(min + f * (max - min));
};
return Object.freeze({
maxInt,
/**
* Produce a random int
*/
randomInt,
/**
* Produce a random float
*/
randomFloat,
/**
* Produce a random float
*/
get value() {
return randomFloat();
},
});
};
const rng = createRng(0);
const iters = 1e5;
let hist = {};
for (let i = 0; i < iters; i++) {
const n = rng.randomInt(0, 10);
hist[n] = (hist[n] || 0) + 1;
}
console.log(hist);
hist = {};
for (let i = 0; i < iters; i++) {
const n = crypto.randomInt(0, 10);
hist[n] = (hist[n] || 0) + 1;
}
console.log(hist);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment