While browsing around these forums, lately i've encountered alot of posts about Random numbers and random number generators. I'm here today to share a piece of code that we use very successful in our engine for fast and efficient random number generation. The source code is mainly thanks to Matthew Jackson who is collaborating with me in making the engine into a reality. Without further ado, here is the source, it is a 63 bit random number generator.

- Basic Feature List

~ ability to get and set the current seed~ ability to get random numbers between min and max (long)~ ability to get random numbers between min and max (float)

/** * Default Constructor which generates the seed based on system time. */publicRandom(){seed = System.nanoTime(); }

/** * * @param seed The value to start the random number generator. * A seed value of 0 generates a random seed based on system time * which is the same as the default constructor. * */publicRandom(longseed){if (seed == 0){seed = System.nanoTime(); }this.seed = seed; }

Hope it helps you all for any game creation needs. All comments/contributions to the source are welcome!

EDIT2 - I've generated and added a bitmap image for comparison of "randomness" of this versus java's random generator. Basically a pixel value is picked randomly in the range of 0 to 255. The images are 512 X 512 in size. Left is the above Generator, Right is Java's Generator.

How much randomness do you get out of such a system? It does not look like it would work very well. Is it based on an existing rng scheme?

It's an XORShift RNG (A subset of a Linear Feedback Shift Register [LFSR]) . The system is extremely fast at generating pseudo-random numbers and has been in use for a long time. The numbers that are being used for the shifts aren't just off the top of your head. They are picked to create a maximal 64 bits shift register (63 bits are caused from the absolute function, effectively doubling up the numbers that can be returned therefore halving the actual variation in numbers returned).

With LFSRs the sequence of numbers will eventually repeat. To be maximal means every single combination of bits (apart from the all zeroes) will appear before the sequence repeats. This means there are a total 9,223,372,036,854,775,808 numbers in a 64-bit LFSR that will appear before the sequence repeats itself when using a maximal sequence.

This system can be "cracked" with a given sample size so you wouldn't use it alone in a security system but there are security algorithms based off it.

However for the sake of games programming, 99 times out of 100 your random number generator doesn't need to be secure.

If you want more information there is plenty of information on the topic all over Google.

I hope this enlightens some of you who haven't seen this type of RNG before.

FOLLOW UP! In a moment of work avoidance I ran the corrected version above through SmallCrush and it performs worse than a good LCG. Specifically it fails 8 out of 15 tests. Didn't bother with BigCrush. Results here: http://pastebin.java-gaming.org/c9c0d928f22

Changing the values to the one mentioned in the paper as being on of his favorites [13, 17, 5], drops the failure rate to 3. Summary (SmallCrush only): 1 BirthdaySpacings eps 6 MaxOft eps 8 MatrixRank eps

I use a m sequence with a plain counter. I then add these before using them. This makes it non linear but adds a bias in the last bit. Not much though. Also the period becomes 2^128-1. Not that you need that kind of period.

I have no special talents. I am only passionately curious.--Albert Einstein

I took over because I wrote the RNG and to be fair the OP shouldn't have posted it because he knew none of the theory behind it so I came in to answer and clarify things for some of the posters.

As for the period as you said it is 2^64 - 1 (Maximal) which is why I chose these numbers. As for the statistical properties, I didn't realise that different choices in numbers could effect it. I will have to read up and better understand all these properties. As for now, I will change the values to your last suggested ones and have the OP correct it in the code.

Edit: I'm not having a go at the OP there he is trying to do the right thing by the community and provide you guys with some working code =)

That second line has a typo: seed >> 7 must be seed >>> 7. The signed instead of unsigned shift breaks everything. I'd strongly recommend losing the Math.abs(seed - 1) and just return seed. It's a significant performance hit that doesn't buy you anything.

On the whole I'd really suggest using a 32-bit xorshift instead of 64-bit. You just don't need the long period (or rather is massively unlikely...and if you do you should know why...and you should probably rethink what you're doing if you need upward to ~3 billion random number to make a decision in a video game). On 64-bit VMs the performance will be similar, but not so for 32-bit...which is a sad reality. Android? I'll let people that have a clue respond here.

Cheers again Roquen, updated the code accordingly. As far as Android goes, haven't really had a bottleneck with using the above version, then again, we only get something like 80FPS on Android compared to 4000FPS PC (The whole engine). Might look into a 32 bit version instead.

This may not be true but isn't Math.abs() call extremely fast? I know it is on C++ (normally translates to a single instruction on modern hardware) but have't really bothered to benchmark java, always assumed its about the same.

Speed is always relative. In this case you have a dependency chain of length 6 which is being extended to 9 (negate, conditional move, subtract)...so it's 50% longer. Luckily there is no reason to prefer low-bits over high bits, so you could replace the return statement with: return (seed >>>1); if you want to stick with unsigned 63 bit results (which only pushes the chain length up to 7, or ~17% longer).

My opinion is to return 64-bit results and let call-site do any fix-up they need locally. Personally the vast majority of my random number generation is for some fixed number of bits. But having said that, when inlined the two shift sequence would be folded into one given the proposed change above.

I see you took out the return Math.abs(seed - 1); That made the RNG scheme seem dubious. There is no reason why you could not get 64 bits out of this scheme in Java if it works for a 64 bit unsigned int. (And it sounds like there is no need to throw out any bits.) I look into my crystal ball and see someone adding that line to get results in the domain [0, 2^63 - 1] instead of any 64 bit signed number other than zero. If that's the case you're unnecessarily limiting yourself to one fewer bit of randomness per call. You also changed a comment to read "Period is 2^64 - 1 " instead of "Max period is 2^64 - 1", which is good. I was going to say the minimum period is more important, but you've already corrected the comment and the old code that broke it. There are still a few things you should change.

Note 1: Although I can't get two calls in a row to return the same nano time on my computer, that might not be the case on all platforms. Return values for System.nanoTime() can be as impercise as currentTimeMillis() which may not even have millisecond precision. It's probably not likely that a computer is fast enough to create two objects but doesn't have a timer accurate enough to return different values in that time span, but maybe there's a small chance. I fixed something else that's far less likely. nanoTime() could return zero, so that could create a bad Random instance.

Note 2: Is this the behavior you want for bad min/max values?

Note 3: Changed it to throw an exception to prevent zero values. I changed the constructor behavior, too, but maybe you wanted zero to be a special value. You can choose one method or the other (or allow zero seeds), but setSeed should probably be consistent with the constructor.

Throwing an exception is exposing an implementation detail. As long as the code does (we do this), then it's perfectly to contract. One common thing to do, say in procedural generation, is to use a pretty poor hashing function to seed a PRNG so zero is a pretty reasonable seeding value. The procedural content generator really should care about any specifics of whatever PRNG it ends up using.

@UltimateSin: I forgot to mention that the various shifting constants (and their permutations) only insure full period and not any statistical properties.

I agree in this case it exposes implementation details and have no interest in arguing one way or another, but I disagree that throwing certain exceptions exposes implementation details. Though in this case it does. But I don't think you can avoid exposing implementation details if that were your only change. This class is not a generic stream of random bits and already abuses that problem. For example, there is no differentiation between seed and state. The fact that getState() getSeed() returns a long instead of an instance of RngState<Random> also carries that problem.

I mainly used the exception to convey an idea (there were no doc comments and people would have just ignored a "don't use zero" statement.) It's probably ...

Sorry, I walked away from the computer, forgot my train of thought for this post, and remembered my original train of thought for my previous post. My original thoughts were that the contract of setSeed should be that only values returnable by getSeed should be considered valid. A user of this class would only choose arbitrary seeds for the instantiation of the object (in which case the contract could reasonably be implementation specific because it is a constructor). All calls to setSeed should assume that the user knows they have a valid seed value, whether that key was obtained from getSeed() or obtained using a static utility function or hardcoded by the programmer. Under that contract, no implementation details are exposed in ordinary methods.

(The setSeed/getSeed name bugs me a lot more now. Seed should be changed to state.)

My verbage was unclear: I wasn't talking about exceptions in general, only in these kinds of situations. WRT: seed vs. state. Two distinctly different notions...they only get blurred in short-period generators were state is representable by (say) a 32 or 64 bit integer. If you want to be nit-picky, then you can't really get a seed, only a state...it's simply a pragmatic choice to name the methods so...and it would be pretty unclear to people that don't understand the "guts" of generators to have "setSeed" & "getState" for these short period generators.

For "NON-GAMING PURPOSES ONLY". In a moment of work avoidance I added a Weyl generator to the above (making it an XorWOW) and ran it through Crush. It passes all test. As a point of reference, the famous Mersenne twister (on it's own) will fail 2.

I have no idea why the over rated, slow and quite poor MT is so popular. It doesn't even fix the problem it was suppose to solve. That is the hyperplanes problem, it just has a lot of them. Weyl generator + m sequence, done. Faster, no hyperplanes and great for serious random number. The only thing you shouldn't use it for is crypto stuff.

I have no special talents. I am only passionately curious.--Albert Einstein

I think it's because it has the one of most cool names is about the only reason. I want to cry every time I see a game using MT. I slowly working on a runtime game PRNG wiki page so HOPEFULLY nobody here will fall into that trap.

The "magic" values of 21, 35 and 4 have been found to produce good results. With these values, the generator has a full period of 264-1, and the resulting values pass Marsaglia's "Diehard battery" of statistical tests for randomness4. L'Ecuyer & Simard (2007)5 also found that values of 13, 7 and 17 fail on only 7 of the 160 tests of their BigCrush battery (for comparison, java.util.Random fails on 21, while crypographic generators such as Java's SecureRandom— as indeed we might expect— generally pass all tests).

Remember that all of this statistical testing is a tangent and is of about zero interest for a game runtime. Quickly tossing the [21,35,4] numbers in (without a Weyl) and it fails 2 SmallCrush tests where the current [13,7,17] only fails one.

Seriously, I understand were you're coming from. That's why I'm doing a first pass at a wiki page.

The problems with java.util.Random isn't one of quality. The quality of its generator is poor, but it is perfectly adequate for virtually all game runtime usage. I most often use a LCG32 which is poorer quality...but it's sufficient most of the time. It's the design of Random that's the problem...it doesn't properly target any specific use-case. You can get sufficient quality much faster and even vastly superior quality faster. So the bottom line is that you're paying a higher than needed cost. If you don't generate many random numbers then it's not really a concern. (I'm ignoring dimensions here)

On the topic of quality, the only reason to consider it here is the notion of maximizing what you get for a given fixed cost. Even if that is only addressing programmer conform-zone issues...it's for "free" so why not choose the known constants that perform the best.

WRT: The link you provided. The thing is there are thousands of 64-bit xorshift and I "know" that none of them alone will pass Crush or BigCrush. No LSFR random number generator will (including MT). DIEHARD (the quoted test) is dated and SmallCrush contains the same tests. It fails SmallCrush simply because that test-suite has been updated to require more computations per test than DIEHARD.

I think this was a reply to the message which I deleted soon after posting it. Soon after posting it appeard to me that it isn't of general interest and that I don't want to stir up more discussion, so I deemed me better to delete it. Sorry for writing it in the first place. Random numbers are a wide field, require in depth math and computational science knowledge and I assume my own level in those domains is about as good to decide which RNG is good for my purpose, or to see if code is efficient. So I want to leave the expert discussion to you and those who know more. For myself I conclude:

- Random() is usually good enough for me needs (i.e. I made no mistakes in my past projects, that need to be fixed).- I used mersenne twister in my C/C++ programs and learned that it is inefficient, but most likely still good enough.- My choice to use SecureRandom() for high quality random numbers in Solarex was right, since performance isn't an issue there, but the quality of the number sequence is.- Xor-Shift is an interesting method which I didn't know before, since it's fast and passes standard tests (i.e. it doesn't have any obvious fails)

... and more I don't need to know for my projects. Sorry for stirring up more discussion than needed

For the record and for completeness. Using a xor shift scheme with the correct constants produces m-sequences. These have clearly defined randomness properties. They are heavily used for testing binary circuits and for communications etc. They have nice mathematics behind them and have all sorts of properties that make them useful for many things.

In short we know a lot about them.

However random here is typically defined as the Spectrum it produces should be white (or the autocorrelation function should be a Dirac delta). Alternatively an 8 bit register will produce every 8 bit number once and only once except 0. Clearly this is not true random. For somethings this is good. For example with stochastic integration you get faster convergence because successive numbers clump less than true random.

If non of that matters to you, then a simple m-sequence generator is perhaps going to be the easiest and fastest by a long shot.

For the few of us doing some scientific simulations, we perhaps want to break up some of the m-sequence structure. This is most easily done with a the previously mentioned weyl generator and then add the results together. This gives nonliterary and passes every test (or not) the same as true random. It is also just 2 extra additions compared to a m-sequences. It is often even faster than LGC because % tends to be pretty slow.

For crypto... well stop reading here for start. Your doing it wrong.

I have no special talents. I am only passionately curious.--Albert Einstein

java-gaming.org is not responsible for the content posted by its members, including references to external websites,
and other references that may or may not have a relation with our primarily
gaming and game production oriented community.
inquiries and complaints can be sent via email to the info‑account of the
company managing the website of java‑gaming.org