-
-
Save DarrenSem/640f0716a6be14ee0ac7a338051a4bee to your computer and use it in GitHub Desktop.
// randomWithSeed.js - use rnd() after rnd = createRandomWithSeed( seed [0+] = Date.now() ) - Math.random is less than 2x faster than this 'good enough' SEED-able version | |
let createRandomWithSeed = seed => { | |
seed = Math.abs(isNaN(seed) ? Date.now() : seed); | |
return () => { | |
seed = (seed * 9301 + 49297) % 233280; | |
return seed / 233280; | |
}; | |
}; | |
// R=s=>(s=Math.abs(isNaN(s)?Date.now():s),_=>(s=(9301*s+49297)%233280,s/233280)); | |
// ^ 79 chars minified (FASTER than the "Modulus 4294967296" combo, but it visibly repeats earlier) | |
let createRandomWithSeed_SLOWER_BUT_LONGER_PERIOD = seed => { | |
seed = Math.abs(isNaN(seed) ? Date.now() : seed); | |
return () => { | |
seed = (seed * 1664525 + 1013904223) % 0x100000000; // 0x100000000 (2 ^ 32) = 4294967296 | |
return seed / 0x100000000; | |
}; | |
}; | |
// R=s=>(s=Math.abs(isNaN(s)?Date.now():s),_=>(s=(1664525*s+1013904223)%4294967296,s/4294967296)); | |
// ^ 95 chars minified (SLOWER than the "Modulus 233280" combo, but this does not visibly repeat as early) | |
// based on http://indiegamr.com/generate-repeatable-random-numbers-in-js/ , e.g. http://demos.indiegamr.com/jumpy/seededRandomLevel/?seed=1.57251 | |
// ( found via https://github.com/bgrins/TinyColor/blob/v2/src/random.ts ) | |
let seededRandom_original = (max, min) => { | |
max = max || 1; | |
min = min || 0; | |
Math.seed = (Math.seed * 9301 + 49297) % 233280; | |
var rnd = Math.seed / 233280.0; | |
return min + rnd * (max - min); | |
}; |
Also note to self:
Most examples of RNG with seed (including, but not only, MT) seem to follow a pattern of having a Class or at least a factory function.
So instead of
let randomWithSeed = ( seed = Math.seed ?? +new Date ) => {
...do things with (and to) seed / Math.seed (which limits us to just one randomizer :-( )
I should instead use something like
let createRandomWithSeed = ( seed = Math.seed ?? +new Date ) => {
return () => {
...do things with only this closure's seed, and not Math.seed which is not actually needed -- and also now this is obviously more scalable...
};
}
PS: +new Date
might minify smaller but it is WAY SLOWER than Date.now()
!
Also note to self:
Most examples of RNG with seed (including, but not only, MT) seem to follow a pattern of having a Class or at least a factory function. So instead of
let randomWithSeed = ( seed = Math.seed ?? +new Date ) => { ...do things with (and to) seed / Math.seed (which limits us to just one randomizer :-( )I should instead use something like
let createRandomWithSeed = ( seed = Math.seed ?? +new Date ) => { return () => { ...do things with only this closure's seed, and not Math.seed which is not actually needed -- and also now this is obviously more scalable... }; }PS:
+new Date
might minify smaller but it is WAY SLOWER thanDate.now()
!
AKA this:
let createRandomWithSeed = (seed) => {
seed = Math.abs(seed);
if(isNaN(seed))seed = Date.now();
return () => {
seed = (seed * 9301 + 49297) % 233280;
return seed / 233280;
};
};
// TODO: try these other 2 sets of numbers:
// With Multiplier = 1664525, Increment = 1013904223, modulus = 0x100000000 (2 ^ 32) = 4294967296
// With Multiplier = 1103515245, Increment = 12345, modulus = 0x80000000 (2 ^ 31) = 2147483648
let SEED = 42;
let rnd = !!"RANDOM_WITH_SEED" ? createRandomWithSeed(SEED) : Math.random;
console.log( rnd() );
let rndA = createRandomWithSeed(7);
console.log( rndA() );
let rndB = createRandomWithSeed(7);
console.log( rndB(), rndB() );
console.log( rndA() );
console.log( rnd() );
^ curious what timing difference if any due to the signature being () instead of (seed)
Timing test showed "2147483648" (3rd combo) to be faster than "4294967296" (2nd combo)... but visible tests show 3rd repeats very early.
IN CONCLUSION, if PERFECTION re. periodization is most important use 2nd combo (4294967296) else 1st (233280, same speed as 2147483648 aka 3rd combo)
let createRandomWithSeed_FASTER_REPEATS_EARLY=s=>(s=Math.abs(isNaN(s)?Date.now():s),_=>(s=(9301*s+49297)%233280,s/233280));
let createRandomWithSeed_SLOWER_REPEATS_LATER=s=>(s=Math.abs(isNaN(s)?Date.now():s),_=>(s=(1664525*s+1013904223)%4294967296,s/4294967296));
// let rnd = createRandomWithSeed(SEED); // then use rnd() instead of Math.random()
Note to self: the choices for the 3 numbers can have big impact on results. For one thing modulus # should likely be large and a power of 2 (faster processing esp. in modern CPUs).
For example, ChatGPT suggested these 2 combos:
With modulus = 0x100000000 (2 ^ 32), Multiplier=1664525, Increment=1013904223
With modulus 2 ^ 31, Multiplier=1103515245, Increment=12345
(And do not haphazardly mix-and-match MAGIC NUMBERS -- they were fine-tuned(?))
🤔