Skip to content

Instantly share code, notes, and snippets.

@5310
Last active April 8, 2021 03:47
Show Gist options
  • Save 5310/9ffee50db776bdc6ad0e to your computer and use it in GitHub Desktop.
Save 5310/9ffee50db776bdc6ad0e to your computer and use it in GitHub Desktop.
MurmurHash driven single-use seed-based pseudo-random number generator. And also a Standard Raised Cosine probabilistic distribution-based map.

This quick Gist shows how to use the fast non-cryptographic hash function MurmurHash to generate a single purely deterministic random numbers based off a of specific seed. I'm borrowing the perezd/node-murmurhash NPM port.

The idea is to deterministically recreate the same set of numbers from a small number of seeds with algorithms that internally use these seeds (in any order, note) by modifying them with constant but unique salts of their own. MurmurHash is fast enough for use in games (for me).

The other half of this Gist contains a standard form (because the custom bound and mode is pointless with a simple general-purpose map function, and the curvature cannot be varied anyway) of the "Gaussianesque" Raised Cosine Distribution, which is conveniently finite and enough for my example use. It maps uniform random numbers generated by the MurmurHash prng to the mentioned distribution, between 0 to 1 in either case.

var seed = "Steve";
var value = rngMap ( rngStdRaisedCosine( rngMurmur( seed+"height" ) ), 4, 7 );
console.log( seed+"'s is "+Math.floor(value)+"'"+Math.round( (value % 1) * 12 )+'" tall.');
/* Directly copied from the global and NPM module version at https://github.com/perezd/node-murmurhash for convenience. */
(function(){
var _global = this;
/**
* JS Implementation of MurmurHash2
*
* @author <a href="mailto:[email protected]">Gary Court</a>
* @see http://github.com/garycourt/murmurhash-js
* @author <a href="mailto:[email protected]">Austin Appleby</a>
* @see http://sites.google.com/site/murmurhash/
*
* @param {string} str ASCII only
* @param {number} seed Positive integer only
* @return {number} 32-bit positive integer hash
*/
function MurmurHashV2(str, seed) {
var
l = str.length,
h = seed ^ l,
i = 0,
k;
while (l >= 4) {
k =
((str.charCodeAt(i) & 0xff)) |
((str.charCodeAt(++i) & 0xff) << 8) |
((str.charCodeAt(++i) & 0xff) << 16) |
((str.charCodeAt(++i) & 0xff) << 24);
k = (((k & 0xffff) * 0x5bd1e995) + ((((k >>> 16) * 0x5bd1e995) & 0xffff) << 16));
k ^= k >>> 24;
k = (((k & 0xffff) * 0x5bd1e995) + ((((k >>> 16) * 0x5bd1e995) & 0xffff) << 16));
h = (((h & 0xffff) * 0x5bd1e995) + ((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16)) ^ k;
l -= 4;
++i;
}
switch (l) {
case 3: h ^= (str.charCodeAt(i + 2) & 0xff) << 16;
case 2: h ^= (str.charCodeAt(i + 1) & 0xff) << 8;
case 1: h ^= (str.charCodeAt(i) & 0xff);
h = (((h & 0xffff) * 0x5bd1e995) + ((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16));
}
h ^= h >>> 13;
h = (((h & 0xffff) * 0x5bd1e995) + ((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16));
h ^= h >>> 15;
return h >>> 0;
};
/**
* JS Implementation of MurmurHash3 (r136) (as of May 20, 2011)
*
* @author <a href="mailto:[email protected]">Gary Court</a>
* @see http://github.com/garycourt/murmurhash-js
* @author <a href="mailto:[email protected]">Austin Appleby</a>
* @see http://sites.google.com/site/murmurhash/
*
* @param {string} key ASCII only
* @param {number} seed Positive integer only
* @return {number} 32-bit positive integer hash
*/
function MurmurHashV3(key, seed) {
var remainder, bytes, h1, h1b, c1, c1b, c2, c2b, k1, i;
remainder = key.length & 3; // key.length % 4
bytes = key.length - remainder;
h1 = seed;
c1 = 0xcc9e2d51;
c2 = 0x1b873593;
i = 0;
while (i < bytes) {
k1 =
((key.charCodeAt(i) & 0xff)) |
((key.charCodeAt(++i) & 0xff) << 8) |
((key.charCodeAt(++i) & 0xff) << 16) |
((key.charCodeAt(++i) & 0xff) << 24);
++i;
k1 = ((((k1 & 0xffff) * c1) + ((((k1 >>> 16) * c1) & 0xffff) << 16))) & 0xffffffff;
k1 = (k1 << 15) | (k1 >>> 17);
k1 = ((((k1 & 0xffff) * c2) + ((((k1 >>> 16) * c2) & 0xffff) << 16))) & 0xffffffff;
h1 ^= k1;
h1 = (h1 << 13) | (h1 >>> 19);
h1b = ((((h1 & 0xffff) * 5) + ((((h1 >>> 16) * 5) & 0xffff) << 16))) & 0xffffffff;
h1 = (((h1b & 0xffff) + 0x6b64) + ((((h1b >>> 16) + 0xe654) & 0xffff) << 16));
}
k1 = 0;
switch (remainder) {
case 3: k1 ^= (key.charCodeAt(i + 2) & 0xff) << 16;
case 2: k1 ^= (key.charCodeAt(i + 1) & 0xff) << 8;
case 1: k1 ^= (key.charCodeAt(i) & 0xff);
k1 = (((k1 & 0xffff) * c1) + ((((k1 >>> 16) * c1) & 0xffff) << 16)) & 0xffffffff;
k1 = (k1 << 15) | (k1 >>> 17);
k1 = (((k1 & 0xffff) * c2) + ((((k1 >>> 16) * c2) & 0xffff) << 16)) & 0xffffffff;
h1 ^= k1;
}
h1 ^= key.length;
h1 ^= h1 >>> 16;
h1 = (((h1 & 0xffff) * 0x85ebca6b) + ((((h1 >>> 16) * 0x85ebca6b) & 0xffff) << 16)) & 0xffffffff;
h1 ^= h1 >>> 13;
h1 = ((((h1 & 0xffff) * 0xc2b2ae35) + ((((h1 >>> 16) * 0xc2b2ae35) & 0xffff) << 16))) & 0xffffffff;
h1 ^= h1 >>> 16;
return h1 >>> 0;
}
var murmur = MurmurHashV3;
murmur.v2 = MurmurHashV2;
murmur.v3 = MurmurHashV3;
if (typeof(module) != 'undefined') {
module.exports = murmur;
} else {
var _previousRoot = _global.murmur;
murmur.noConflict = function() {
_global.murmur = _previousRoot;
return murmur;
}
_global.murmur = murmur;
}
}());
var rngMap = function( x, a, b ) {
return a + ( b - a ) * x;
};
var rngStdRaisedCosine = function( x ) {
// Standard version with mu = s = 0.5
// pdf: con(PI*(2*x-1)) + 1
// cdf: ( sin(PI*(2*x-1)/2) + 1 ) / 2
// inverse cdf: ( PI - 2*asin(1-2*x) ) / 2*PI
return ( Math.PI - 2 * Math.asin( 1 - 2 * x ) ) / ( 2 * Math.PI );
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment