Created
June 2, 2014 17:33
-
-
Save laurentsenta/15d7f6fcfc2987176b54 to your computer and use it in GitHub Desktop.
Seedable Random Number Generator in Typescript
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| export class RNG { | |
| private seed:number; | |
| constructor(seed:number) { | |
| this.seed = seed; | |
| } | |
| private next(min:number, max:number):number { | |
| max = max || 0; | |
| min = min || 0; | |
| this.seed = (this.seed * 9301 + 49297) % 233280; | |
| var rnd = this.seed / 233280; | |
| return min + rnd * (max - min); | |
| } | |
| // http://indiegamr.com/generate-repeatable-random-numbers-in-js/ | |
| public nextInt(min:number, max:number):number { | |
| return Math.round(this.next(min, max)); | |
| } | |
| public nextDouble():number { | |
| return this.next(0, 1); | |
| } | |
| public pick(collection:any[]):any { | |
| return collection[this.nextInt(0, collection.length - 1)]; | |
| } | |
| } |
Have you considered submitting this to npm, or is it too simple?
In reference to @makoConstruct suggestions:
- it is not necessary to change the value, the modulus already reduces the range to "less than" so the division is correct as written
- it is however critical to change the 'round' to a 'floor' (because otherwise the integer value '0' is picked only half as many times as other values)
I ran a quick test to check distribution after this change:
// NOTE: I modified the class to static, the logic remains the same
RNG.seed = 12345;
let r:Array<number> = new Array<number>(11);
for(let i:number = 0; i < 11; i++)
r[i] = 0;
for(let i:number = 0; i < 1000000; i++)
r[RNG.nextInt(0,10)]++;
for(let i:number = 0; i < 11; i++)
console.log(i + " = " + r[i]);
which results in values:
0 = 100004
1 = 100016
2 = 99986
3 = 100000
4 = 100026
5 = 100008
6 = 99956
7 = 100035
8 = 99977
9 = 99992
10 = 0
(Similar tests with different seeds and ranges also produced reasonable looking distributions)
EDIT: I recommend throwing an 'abs' in for the seed too, I think negative seed values might mess up the algebra
When changing to floor, you also have to change pick (remove the -1):
public pick(collection:any[]):any {
return collection[this.nextInt(0, collection.length)];
}
otherwhise the last element is never picked, because max is no longer inclusive.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is going to be biased against drawing min and max. Basically, imagine if min were 0 and max were 2, and imagine how the map of reals from 0 to 2 map through Math.round. 0 to 0.5 will map to 0. 0.5 to 1.5 will map to 1. 1's range, then, is twice as large as 0 (same goes for 2). This is not desirable.
I'd recommend changing
and changing
the output range will now go from min to max-1, and that is fine and ordinary for range specifiers and programmers should generally expect that kind of behavior.