views:

2800

answers:

25

What is a good random number generator to use for a game in C++?

My considerations are:

  1. Lots of random numbers are needed, so speed is good.
  2. Players will always complain about random numbers, but I'd like to be able to point them to a reference that explains that I really did my job.
  3. Since this is a commercial project which I don't have much time for, it would be nice if the algorithm either a) was relatively easy to implement or b) had a good non-GPL implementation available.
  4. I'm already using rand() in quite a lot of places, so any other generator had better be good to justify all the changes it would require.

I don't know much about this subject, so the only alternative I could come up with is the Mersenne Twister; does it satisfy all these requirements? Is there anything else that's better?

Edit: Mersenne Twister seems to be the consensus choice. But what about point #4? Is it really that much better than rand()?

Edit 2: Let me be a little clearer on point 2: There is no way for players to cheat by knowing the random numbers. Period. I want it random enough that people (at least those who understand randomness) can't complain about it, but I'm not worried about predictions. That's why I put speed as the top consideration.

Edit 3: I'm leaning toward the Marsaglia RNGs now, but I'd still like more input. Therefore, I'm setting up a bounty.

Edit 4: Just a note: I intend to accept an answer just before midnight UTC today (to avoid messing with someone's rep cap). So if you're thinking of answering, don't wait until the last minute!
Also, I like the looks of Marsaglia's XORshift generators. Does anyone have any input about them?

A: 

What about Knuth's RNGs?

Andrew Coleson
+32  A: 

Sometimes game developers don't want true randomness and a shuffle bag is more appropriate.

If you do want randomness, the Mersenne twister satisfies your requirements. It is fast, statistically random, has a long period and there are plenty of implementations out there.

Edit: rand() is typically implemented as a linear congruential generator. It's probably best if you make an informed choice of whether or not it's good enough for your purposes.

David Johnstone
Spot on. Without this players will complain _constantly_ about things being too random.
toholio
I read that question already (in fact, it sparked the discussion that led to this question), but I don't think it is the solution. The "hits" here are abstracted away from the player, so players likely wouldn't even notice.
Michael Myers
"Empty body for-loops are Satan's offspring, but let's ignore this for now." +1 for a laugh and a good answer. (ironically, I used to use them alot in Perl...)
Matthew Scharley
+9  A: 

Mersenne Twister is typical in the industry, especially since it lends itself well to SIMD and can be made super fast. Knuth is popular too (thanks, David).

In most game applications speed is really the critical factor, since players are going to complain about low framerate a lot more than they will complain about the fact that there is a slight bias towards generating a 3 whenever it is preceded by a 7, 2, and 9 in that order.

The exception of course is gambling for money, but there your relevant licensing authority will specifically lay out the algorithms that you can use.

Crashworks
They may complain alot (and fairly loudly) that the 3 never seems to come up at all for them. Depends on the usage. True random is good for things like damage, but for things like item drops it makes sense to use a shuffling algorithm like @David Johnstone posted.
Matthew Scharley
True enough; you'll want a different algorithm depending on whether "fairness" or true randomness is more important.
Crashworks
They don't get to actually see the random numbers, but they do notice that sometimes they lose when they don't expect to. (I'm sure it happens the other way too, but they hardly ever seem to notice it.) Perhaps a normal distribution would be better for the combat part.
Michael Myers
Also, most languages have a good off-the-shelf implementation of MT available, and probably one under a licence that doesn't preclude you from incorporating it in commercial software.
ConcernedOfTunbridgeWells
+3  A: 

Mersenne Twister is very good, and it's fast as well. I used it in a game and it's not hard at all to implement or use.

The WELL random algorithm was designed as an improvement over the Mersenne Twister. Game Gems 7 has more info. on it, if you can borrow that or have it.

On that WELL page I linked you to, the number is the period of the algorithm. That is, you can get 2^N - 1 numbers before it needs reseeding, where N is: 512, 1024, 19937, or 44497. Mersenne Twister has a period of N = 19937, or 2^19937 - 1. You'll see this is a very large number :)

The only other thing I can point out is that boost has a random library, which you should find useful.

In response to your edit, yes the Twister or WELL is that much better than rand(). Also, the old modulus trick harms the distribution of the numbers. Even more reason to use boost :)

GMan
Is boost worth it just for random numbers? We aren't using it at all right now.
Michael Myers
Boost is very worth it. Clean C++ use (just check out the page), along with all the other things boost offers, like smart pointers, boost::functions, lambda's, threads, etc... I basically treat boost like the STL. Once it's set up, which isn't too difficult, you'll be able to forget it's even there, and be glad when you do need it.
GMan
That's weird, this answer didn't get me rep. I'm not a rep whore but is that a bug or something?
GMan
GMan - I'm guessing you already have 200 rep today (well, for the day that finished 12 minutes ago)
David Johnstone
Oh yeah I forgot about that. What time (and timezone) does a day end?
GMan
It ends at 00:00 GMT, which is 18 minutes ago by my clock.
Michael Myers
Ah, good to know, thanks.
GMan
Remember that boost is not either or. You just include the single header file you need, and that is all you get. Most boost libraries is even header-only libraries, so no need to link to anything, everything is in the header file.
bjarkef
GMan: You have a typo in the last phrase of the first paragraph. The period of MT 19937 is not 19937, but 2^19937 − 1
phresnel
Hm, let's call it a semi-typo :P
GMan
+1 for being the first answer I saw that recommended boost
rmeador
+4  A: 

In a real-time game, there's no way for a player to determine the difference between a "good" generator and a "bad" one. In a turn-based game, you're right--some minority of zealots will complain. They'll even tell you stories, in excruciating detail, of how you ruined their lives with a bad random number generator.

If you need a bunch of genuine random numbers (and you're an online game), you can get some at Random.org. Use them for turn-based games, or as seeds for real-time games.

Nosredna
It's semi-real-time. For example, in combats, the "dice" are re-rolled every 5 game days. Combats generally last a game month or more, though.
Michael Myers
Oh, and it's not web-based, so random.org isn't an option.
Michael Myers
It's totally possible to experience true-random frustration in a real-time game. If, every time you hit the "attack" button, your sword misses the giant rat, you will be just as pissed as if you had to spend a "turn" on every miss.
ojrac
@ojrac. That's true, you can experience it. What I meant is that a user can't identify the random numbers coming at him. But you're correct that the player often doesn't want true random numbers. He wants fun numbers, which may have an entirely different distribution. But the programmer can solve that problem by post-processing "real" random numbers into different sequences or distributions.
Nosredna
random.org still might be a choice even if your game is not web based. If you require any kind of internet connection, you can either connect to the random.org on demand or you can request a long list of random numbers in a spesified range, store them and use as needed. If you run out of pre-fetched randoms, you can fall back to good-old rand(). One last note: random.org uses the static noise from the sound input so you may try to use that also as random seed.
BYK
A: 

I used this class before and it is super-easy to use.

A Mersenne Twister Class

It is a class created from the original C code of the inventor of Mersenne Twister algorithm.

AraK
A: 

I haven't done a comparison in a long time, but here's a random collections of links, to other people's comparisons:

(I'll pop back in with some more later...)

Stobor
Is there any nice comparison table (that includes speed)? I couldn't find one.
Michael Myers
@mmyers: the dieharder is a speed/quality comparsion... But it's a package where you can run the comparison benchmarks yourself on your platform... There's some results on their website, though.
Stobor
Is your collection of links really "random?"
Nosredna
+2  A: 

I'm a fan of Isaac, unlike mersense twister, it's crypographically secure (you *can't crack the period by observing the rolls)

IBAA (rc4?) is also one that is used by blizzard to prevent people from predicting the random number used for loot rolls.. I imagine something similar is done w/ diablo II when you are playing off of a battle.net server.

*can't within any reasonable timeframe (centuries?)

Ape-inago
+20  A: 

Sometimes you just have to go insane.

Ólafur Waage
Hmmm... Interesting idea, but I don't think it's *quite* practical.
Michael Myers
It's a fun addition to the list :)
Ólafur Waage
that's brilliant!
Ed Woodcock
that is simply amazing
Gregory Mostizky
A: 

This doesn't contradict previous answers but does provide a bit of insight into why Mersenne Twister is highly regarded in C++:

A Proposal to Add an Extensible Random Number Facility to the Standard Library

Section H gives a rough overview of the pros and cons of more algorithms than you're ever likely to come across, and the whole paper addresses them from the perspective of a C++ programmer.

Kylotan
+1  A: 

Buy a cheap webcamera, a ionizing smoke detector. Disassemble both of them, smoke detector contain little radioactive material - a source of gamma waves - which will result in firing photons at your webcamera. That's your source of true randomness :)

Vexatus
Again, I don't really care about true randomness. I just want it pretty close and pretty fast.
Michael Myers
What's faster than a photon?
Nosredna
This idea generates events at random times, but not random bits on demand. Plus the smoke-detector-webcam hardware is going to be seen as a copy-protection dongle by the customer base.
Jeffrey Sharp
I'd buy the game just because it has a "copy-protection" dongle with a radioactive warning sticker on it!
Grant Peters
+2  A: 

http://xkcd.net/221/

StuperUser
And the Dilbert one about the troll that always generates 9. ("Is that really random?" "That's the trouble with randomness; you can never be sure.") Neither is particularly helpful, though.
Michael Myers
+8  A: 

George Marsaglia has developed some of the best and fastest RNGs currently available Multiply-with-carry being a notable one for a uniform distribution.

locster
It was seeing Marsaglia's criticism in the Wikipedia article on Mersenne Twister that actually made me ask this question. Does anyone actually use these, though?
Michael Myers
Not sure how widespread use of these is but I used Marsaglia's xor-shift RNG in my pet project and released the RNG as:http://www.codeproject.com/KB/cs/fastrandom.aspxSubsequently this code was ued in a RNG code library:http://www.codeproject.com/KB/recipes/Random.aspxAnd I also was asked to modify the licensing for it to be used in some project at Novell running on Mono.
locster
I accepted this because in my simple tests, the XOR-shift generator proposed by Marsaglia was by a fair margin the fastest of the competitors. I will also consider switching to WELL512 at some point; I've abstracted all calls to `rand` into an encapsulating class so it's very easy to change the implementation.
Michael Myers
Thanks, I chose it for speed also, while it also passes Marsaglia's suite of RNG tests. Another benefit is that the seed can be reset quickly, whereas other RNGs require an initialisation routine to be run on settign the seed. This is a useful feature if you need to re-generate the exact same sequence of numbers (start with the same seed) and need to switch the seed frequently.
locster
+1  A: 

Based on the random number generator by Ian C. Bullard:

// utils.hpp
namespace utils {
    void srand(unsigned int seed);
    void srand();
    unsigned int rand();
}

// utils.cpp
#include "utils.hpp"
#include <time.h>

namespace {
    static unsigned int s_rand_high = 1;
    static unsigned int s_rand_low = 1 ^ 0x49616E42;
}

void utils::srand(unsigned int seed)
{
    s_rand_high = seed;
    s_rand_low = seed ^ 0x49616E42;
}

void utils::srand()
{
    utils::srand(static_cast<unsigned int>(time(0)));
}

unsigned int utils::rand()
{
    static const int shift = sizeof(int) / 2;
    s_rand_high = (s_rand_high >> shift) + (s_rand_high << shift);
    s_rand_high += s_rand_low;
    s_rand_low += s_rand_high;
    return s_rand_high;
}

Why?

  • very, very fast
  • higher entropy than most standard rand() implementations
  • easy to understand
Armin Ronacher
Ian has an interesting article that compares several generators. http://ianbullard.squarespace.com/journal/2009/4/28/why-you-should-never-use-rand.html
Sean McCauliff
+2  A: 

Mersenne Twister seems to be the consensus choice. But what about point #4? Is it really that much better than rand()?

Check http://ianbullard.squarespace.com/journal/2009/4/28/why-you-should-never-use-rand.html

sdcvvc
Cheers for that link ! :-)
A: 

Here's a link to a collection of articles about random numbers

zooropa
A: 

I want it random enough that people (at least those who understand randomness) can't complain about it, but I'm not worried about predictions.

A-ha!

There's your real requirement!

No one could fault you for using Mersenne Twister in this application.

Marsh Ray
+1  A: 

I'd vote for the Mersenne Twister as well. Implementations are widely available, it has a very large period of 2^19937 -1, is reasonably fast and passes most randomness tests including the Diehard tests developed by Marsaglia. rand() and Co., being LCGs, produce lower quality deviates and their successive values can be easily inferred.

One point of note, however, is to properly seed MT into a state that passes randomness tests. Usually a LCG like drand48() is used for that purpose.

I'd say the MT satisfies all the requirements you've set (provably), and it'd be an overkill to go for something like MWCG imo.

Michael Foukarakis
A: 

Depending on the target OS, you might be able to use /dev/random. It doesn't really require any implementation, and on Linux (and maybe some other operating systems) it's truly random. The read blocks until sufficient entropy is available, so you might want to read the file and store it in a buffer or something using another thread. If you can't use a blocking read call, you can use /dev/urandom. It generates random data almost as well as /dev/random, but it reuses some random data to give output instantly. It's not as secure, but it could work fine depending on what you plan to do with it.

computergeek6
The target OS is Windows, with a Mac port sometime later.
Michael Myers
A: 

Apparently (I forget where I read it, just like I forget where I read that curry is good to prevent altzheimas), taking the absolute value of the checksum of a newly generated GUID is nicely random. It's a large number, and you can use a modulo of it to shrink it down.

So in SQL (my area), this is ABS(CHECKSUM(NEWID())) % 1000

Rob

Rob Farley
Seriously? SQL? He's talking about a GAME.
shoosh
Sure, but if he can quickly generate a guid, then the principle still applies.
Rob Farley
Is GUID generation that fast anyway?
Michael Myers
I'm not sure about the speed, I mentioned it because apparently the randomness is very good. Doing the checksum is likely to be slower. It all comes down to how predictable the results should be.
Rob Farley
A: 

GameRand

VERY fast compared to other algorithms (according to this doc anyways)

Jason
+17  A: 

There are much better choices than Mersenne Twister nowadays. Here is a RNG called WELL512, designed by the designers of Mersenne, developed 10 years later, and an all around better choice for games. The code is put in the public domain by Dr. Chris Lomont. He claims this implementation is 40% faster than Mersenne, does not suffer from poor diffusion and trapping when the state contains many 0 bits, and is clearly a lot simpler code. It has a period of 2^512; a PC takes over 10^100 years to cycle through the states, so it is large enough.

Here is a paper overviewing PRNGs where I found the WELL512 implementation. http://www.lomont.org/Math/Papers/2008/Lomont_PRNG_2008.pdf

So - faster, simpler, created by the same designers 10 years later, and produces better numbers than Mersenne. How can you go wrong? :)

/* initialize state to random bits */
static unsigned long state[16];
/* init should also reset this to 0 */
static unsigned int index = 0;
/* return 32 bit random number */
unsigned long WELLRNG512(void)
   {
   unsigned long a, b, c, d;
   a = state[index];
   c = state[(index+13)&15];
   b = a^c^(a<<16)^(c<<15);
   c = state[(index+9)&15];
   c ^= (c>>11);
   a = state[index] = b^c;
   d = a^((a<<5)&0xDA442D20UL);
   index = (index + 15)&15;
   a = state[index];
   state[index] = a^b^d^(a<<2)^(b<<18)^(c<<28);
   return state[index];
   }
Chris Lomont
That's a good one.
Nosredna
+1, I'll definitely consider this one.
Michael Myers
Good suggestion +1.
DrFalk3n
I waste one evening to understand why my code doesn't work: on 64 bit machines this code procude 64 bit number! Use `sizeof(unsigned long) * 8`
wiso
A: 

I've used the boost random libraries with games in the past. It has mercenne Twister http://www.boost.org/doc/libs/1_39_0/libs/random/index.html

Ryu
+1  A: 

An additional criteria you should consider is thread safety. (And you should be using threads in todays multi-core environments.) Just calling rand from more than one thread can mess with it's deterministic behavior (if your game depends on that). At the very least I'd recommend you switch to rand_r.

geowar
A: 

You know what? Forgive me if you think this answer completely sucks... But I've been (for god only knows what reason...) using DateTime.Now.Milliseconds as a way to get a random number. I know it's not completely random, but it appears to be...

I just couldn't be bothered typing so much JUST to get a random number! :P

baeltazor