Repeatable Random
Everybody knows about Math.random(), and for good reason. It’s pretty much the way to get random numbers in AS3, AS2, and JavaScript other than bizarre alternatives like AS3’s BitmapData.noise(). However, it has one critical problem that arises when you want to repeat a certain test or prevent game cheaters from exploiting the randomizer until they get an “easy” setup or desirable outcome. This problem is the lack of repeatability.
Making a pseudo-random number generator is easy. Making a good one is hard. Here, I’m going to present you with a very basic one for very basic pseudo-random number generation. I don’t remember where I found this one, but there are very many hits if you Google it. If you need something that’s cryptographically strong or to generate really good noise, you should not use this. If you just need to get a few random numbers to generate a game level, randomize starting points of some display objects, or the like, this should be random enough for you.
package { /** * A random number generator * @author Jackson Dunstan */ public class Random { /** Current seed value */ private var __seed:int; /** * Make the random number generator * @param seed Initial seed value */ public function Random(seed:int) { __seed = seed; } /** * Get the current seed value * @return The current seed value */ public function get seed(): int { return __seed; } /** * Set the current seed value * @param seed The current seed value */ public function set seed(seed:int): void { __seed = seed; } /** * Get the next integer in the pseudo-random sequence * @param n (optional) Maximum value * @return The next integer in the pseudo-random sequence */ public function nextInt(n:int = int.MAX_VALUE): int { return n > 0 ? nextNumber() * n : nextNumber(); } /** * Get the next random number in the pseudo-random sequence * @return The next random number in the pseudo-random sequence */ public function nextNumber(): Number { __seed = (__seed*9301+49297) % 233280; return __seed / 233280.0; } } }
The class is really simple. Unlike Math.random(), you need to create an instance of a Random object. This is where you get the opportunity to pass the random “seed”, which determines the sequence of “random” numbers you get out of it. This lends it the desired property of repeatability. So in the case that you want the same random number sequence over and over, simply make sure you pass the same seed to the constructor. For example, you can make sure that the “random” occurrences in a game are the same each time by saving the random seed in the save game file and simply using it again the next time the user loads the game. I’m pretty sure Civilization III does this as I may have attempted to exploit it (as was possible in Civilization II) once or twice. Anyhow, once you have the Random object, you simply call nextNumber() or nextInt() on it to your heart’s content. The sequence of numbers you get back is infinitely long. Here’s a little chunk of code to test this out and make sure that the sequence of numbers is indeed repeatable:
const NUM_VALS:int = 100000; var seed:int = getTimer(); var r:Random = new Random(seed); var ints:Vector.<int> = new Vector.<int>(NUM_VALS); var numbers:Vector.<Number> = new Vector.<Number>(NUM_VALS); for (var i:int = 0; i < 10; ++i) { ints[i] = r.nextInt(100); numbers[i] = r.nextNumber(); } r = new Random(seed); var passed:Boolean = true; for (i = 0; i < 10; ++i) { if (r.nextInt(100) != ints[i]) { passed = false; break; } if (r.nextNumber() != numbers[i]) { passed = false; break; } } trace(passed ? "SUCCESS" : "FAIL");
This always prints “SUCCESS” for me, no matter how I change the seed or how many iterations I put in. This confirms the repeatability. While the above code is in AS3, it can easily and straightforwardly be converted to an AS2 or JavaScript class. Therefore, this should be useful to you wherever on the (client side) web you’re needing repeatable random numbers. Enjoy!
#1 by Og2t on October 21st, 2009 ·
Pretty interesting that * 9301 + 49297) % 233280, never heard of it before… wonder who came up with it and if it really proves seed/iteration dependent.
#2 by Zevan on October 21st, 2009 ·
Nice post… @Og2t this is a linear congruential generator which uses the recurrence relation:
Xn+1 = (aXn + b) mod m
I was messing with these awhile back but couldn’t get decent values for a, b and m…. looks like your a, b and m values make for some pretty good noise (tried this with setPixel()).
In the past I’ve opted for the Tauseworthe RNG (which can be used to draw seeded noise) and have been meaning to port an example of the Mersenne twister
#3 by Zevan on October 21st, 2009 ·
hmmm looks like the tags got stripped out of my post… here is an image of the recurrence relation:
http://upload.wikimedia.org/math/1/4/4/144550627858cb6d44ceb02ba9434317.png
#4 by Subblue on October 21st, 2009 ·
You might find this link interesting about pseudo random number generators: http://www.bobwheeler.com/statistics/Password/MarsagliaPost.txt
#5 by jackson on October 21st, 2009 ·
Thank you all for the good points! Zevan: I’d be really interested to see your results if you ever do port the Mersenne Twister.
#6 by skyboy on November 19th, 2010 ·
I’ve ported it over to AS3: https://github.com/skyboy/AS3-Utilities/blob/master/skyboy/utils/Random.as
I haven’t done extensive testing (ie. checking that the repeating period is the same length), but it looks well enough, and includes a couple helper functions, one being a static function to get results without needing to create an instance yourself (though it will be different between runs).
#7 by Cory on December 3rd, 2009 ·
Hey Jackson– I ported this to Javascript at http://www.mawhorter.net/web-development/another-look-at-repeatable-random
I found this post while doing research for my own (significantly less powerful) repeatable random generator that creates an even distribution, which is linked in that post.
#8 by Alexander on February 2nd, 2012 ·
Very interesting – this also allows for things like tool-assisted speedruns in games (assuming you store the seed serially or somesuch). It also allows for physics engines to be deterministic.
Have you done a test for performance vs. Math.random()?
#9 by jackson on February 2nd, 2012 ·
Yes, those are definitely good uses of the class.
I haven’t performance tested it, but that’s a good idea for a followup article. Thanks for the tip.