diff --git a/jslda.css b/jslda.css index c8e6de6..84fe745 100644 --- a/jslda.css +++ b/jslda.css @@ -5,6 +5,7 @@ div.sidebar div.sidebox { margin: 5px; padding: 10px; } div.top { background-color: #ddd; margin: 5px; padding: 10px; -moz-border-radius: 10px; border-radius: 10px; } div.top #num_topics_control { float:right; } +div.top #random_seed_box { float:right; margin-left: 10; margin-right: 10; margin-top: 2; } div.upload { background-color: #ddd; float: right; padding-right: 20px; border-radius: 10px; margin: 5px; } div.upload div { margin: 8px; } diff --git a/jslda.html b/jslda.html index 5e3f63a..f86a7eb 100644 --- a/jslda.html +++ b/jslda.html @@ -15,6 +15,8 @@ Iterations: 0 + + Train with 25 topics diff --git a/jslda.js b/jslda.js index a1602fc..ae23365 100644 --- a/jslda.js +++ b/jslda.js @@ -1,3 +1,54 @@ +// Seedable, fast RNGs (random number generator). (JS's internal Math.random is not seedable) +// +// Taken from https://github.com/bryc/code/blob/master/jshash/PRNGs.md + +// "Mulberry32 is minimalistic generator utilizing a 32-bit state, originally intended for embedded applications. +// It appears to be very good; the author states it passes all tests of gjrand, and this JavaScript implementation +// is very fast. But since the state is 32-bit like Xorshift, it's period (how long the random sequence lasts before +// repeating) is significantly less than those with 128-bit states, but it's still quite large, at around 4 billion." +function mulberry32(seed) { + let a = seed; + return () => { + a |= 0; + a = a + 0x6D2B79F5 | 0; + let t = Math.imul(a ^ a >>> 15, 1 | a); + t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t; + return ((t ^ t >>> 14) >>> 0) / 4294967296; + }; +} + +// "Similar seeds (e.g. a simple seed of 1 and 2) can cause correlations in weaker PRNGs, resulting in the output +// having similar properties (such as randomly generated levels being similar). To avoid this, it is best practice +// to initialize PRNGs with a well-distributed seed." +// "I propose using a seperate hash function to intiailize the entire state. Hash functions are very good at +// generating seeds for PRNGs from short strings. A good hash function will generate very different results even +// when two strings are similar. xmur3 an example based on MurmurHash3's mixing function. +function xmur3(str) { + let h = 1779033703 ^ str.length; + for (let i = 0; i < str.length; i++) { + h = Math.imul(h ^ str.charCodeAt(i), 3432918353); + h = h << 13 | h >>> 19; + } + return () => { + h = Math.imul(h ^ h >>> 16, 2246822507); + h = Math.imul(h ^ h >>> 13, 3266489909); + return (h ^= h >>> 16) >>> 0; + }; +} + +function getRNG(seed) { + const numSeed = xmur3(seed)(); + const rng = mulberry32(numSeed); + return rng; +} + +var random = getRNG("myRNG"); +Math.random = random + +function setRandomSeed() { + let seed = window.prompt("Reset the RNG with the following seed:", "any string"); + Math.random = getRNG(seed); +} /** This function is copied from stack overflow: http://stackoverflow.com/users/19068/quentin */ var QueryString = function () {