tags:

views:

3016

answers:

8

I need a 'good' way to initialize the pseudo-random number generator in C++. I've found an article that states:

In order to generate random-like numbers, srand is usually initialized to some distinctive value, like those related with the execution time. For example, the value returned by the function time (declared in header ctime) is different each second, which is distinctive enough for most randoming needs.

Unixtime isn't distinctive enough for my application. What's a better way to initialize this? Bonus points if it's portable, but the code will primarily be running on Linux hosts.

I was thinking of doing some pid/unixtime math to get an int, or possibly reading data from /dev/urandom.

Thanks!

EDIT

Yes, I am actually starting my application multiple times a second and I've run into collisions.

+4  A: 

if you need a better random number generator, don't use the libc rand. Instead just use something like /dev/random or /dev/urandom directly (read in an int directly from it or something like that).

The only real benefit of the libc rand is that given a seed, it is predictable which helps with debugging.

Evan Teran
+1 for practicality
orip
Good solution, assuming you're on unix.
Graeme Perrow
On Windows you can use rand_s.
phjr
+7  A: 

Best way is to use another pseudorandom number generator. Mersenne twister (and Wichmann-Hill) is my recommendation.

http://en.wikipedia.org/wiki/Mersenne_twister

I was just about to suggest this generator. I've used it for many scientific modelling problems, and it produces much better results than alternatives I have seen. http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
Akusete
If you have access to a better RNG, why use it to seed the standard one? Just use the better one!
paxdiablo
that was essentially my suggestion :-P
Evan Teran
@Pax: USE another PRNG, not use PRNG as seed!Seeding PRNG with better PRNG, who would do something like that?? :D
Sorry, @sharpsy, just naturally combined question with your answer to get "use another RNG to seed standard one". My bad.
paxdiablo
It seems odd to me that in order to come up with a good pseudo-random number, you need to first come up with a good pseudo-random number. :-)
Graeme Perrow
Mersenne twister is good pseudo-random number generator. It's meant to be used instead of rand() (if 'randomness' is important), not for srand().
+9  A: 

Best answer is to use the Boost randum number stuff.

But if we are talking about rand() and srand()
The best way is just to use time()

int main()
{
    srand(time(NULL));

    ...
}

Every time you start up, time() will return a unique value (unless you start the application multiple times a second). In 32 bit systems it will only repeat every 60 years or so.

I know you don't think time is unique enough but I find that hard to believe. But I have been known to be wrong.

If you are starting a lot of copies of your application simultaneously you could use a timer with a finer resolution. But then you run the risk of shorter time period before the value repeats.

OK So if you you really think you are starting multiple applications a second.
Then use a finer grain on the timer.

 int main()
 {
     struct timeval;
     gettimeofday(&time,NULL);

     // microsecond has 1 000 000
     // Assuming you did not need quite that accuracy
     // Also do not assume the system clock has that accuracy.
     srand((time.tv_sec * 1000) + (time.tv_usec / 1000));

     // The trouble here is that the seed will repeat every
     // 24 days or so.

     // If you use 100 (rather than 1000) the seed repeats every 248 days.

     // Do not make the MISTAKE of using just the tv_usec
     // This will mean your seed repeats every second.
 }
Martin York
Actually, I am starting multiple instances of my app a second :)
Gary Richardson
I saw somewhere solution that works with timeval/gettimeofday.... ah, yes, my own!! You criticised that its VERY BAD, although chances are 1/1000000 that it will repeat (with the same assumptions you made).And yours is 1/60000. So, it's my solution, only worse, and it is presented as yours.
1) Don't assume the clock resolution is 1/1000000. 2) My probability is 1/2147483648 (note the tv_sec). 3) seed repeats is 24 days apart (interaction between apps is also important).
Martin York
Your solution is a well known anti-pattern. My solution is not mine, it is a well known better solution to the one you presented (time to read some books ;-)
Martin York
"seed repeats is 24 days apart"You have 1000 collisions in a second.
I have a 1/1000 probability of a collision in any 1 sec. Where the seed overlaps every 24 days. Which is why I prefer to use time() where seed only overlaps every 60 years.
Martin York
+2  A: 

i suggest you see unix_random.c file in mozilla code. ( guess it is mozilla/security/freebl/ ...) it should be in freebl library.

there it uses system call info ( like pwd, netstat ....) to generate noise for the random number;it is written to support most of the platforms (which can gain me bonus point :D ).

FL4SOF
+2  A: 

On windows:

srand(GetTickCount());

provides a better seed than time() since its in milliseconds.

shoosh
+3  A: 
#include <stdio.h>
#include <sys/time.h>
main()
{
     struct timeval tv;
     gettimeofday(&tv,NULL);
     printf("%d\n",  tv.tv_usec);
     return 0;
}

tv.tv_usec is in microseconds. This should be acceptable seed.

This is a VERY BAD idea. The seed repeats every second. If you start the application multiple times you get a high probability you will get the same seed.
Martin York
VERY BAD? 1/1000000 (yes, that's a one in a milion) chances of repeating? If you feel that lucky, go buy a lottery ticket!9.5367431640625e-07 (less than a usecond)measured with python:from time import timeabs(time()-time())#on windows use clock()-clock()
That's great if you only buy one lottery ticket. But if you are buying 1 lottery ticket a second how often do you get a hit. probability says 36 hits a year. My method 1/2147483648 chance repeated every 24 days, in 1 year that's 1/143165576 hits a year!
Martin York
"chance repeated every 24 days" :DHow did you come up with that?Sec=X+1, USec<1000... You have collision 1000 times in a second.
I use tv_sec: based on the epoch. It repeats every 60 years. Module this by 1000 (I knock of the top bits by multiplying by 1000). This gives you a value that repeats every 24 days. I then insert uSec in the bottom bits. I thus have a time with mili second precision that repeats every 24 days.
Martin York
You are using a micro precision timer that repeats every second.
Martin York
This very discussion is an example of why rand (like cryptography) is difficult to get correct and why people should be using boost::rand and not trying to roll their own. Unless you are an expert on the subject, then there are many pitfalls where the pseudo random numbers become less than random.
Martin York
+7  A: 

This is what I've used for small command line programs that can be run frequently (multiple times a second):

unsigned long seed = mix(clock(), time(NULL), getpid());

Where mix is:

// http://www.concentric.net/~Ttwang/tech/inthash.htm
unsigned long mix(unsigned long a, unsigned long b, unsigned long c)
{
    a=a-b;  a=a-c;  a=a^(c >> 13);
    b=b-c;  b=b-a;  b=b^(a << 8);
    c=c-a;  c=c-b;  c=c^(b >> 13);
    a=a-b;  a=a-c;  a=a^(c >> 12);
    b=b-c;  b=b-a;  b=b^(a << 16);
    c=c-a;  c=c-b;  c=c^(b >> 5);
    a=a-b;  a=a-c;  a=a^(c >> 3);
    b=b-c;  b=b-a;  b=b^(a << 10);
    c=c-a;  c=c-b;  c=c^(b >> 15);
    return c;
}
Jonathan Wright
+1  A: 

The real question you must ask yourself is what randomness quality you need.

libc random is a LCG

The quality of randomness will be low whatever input you provide srand with.

If you simply need to make sure that different instances will have different initializations, you can mix process id (getpid), thread id and a timer. Mix the results with xor. Entropy should be sufficient for most applications.

Example :

struct timeb tp;
ftime(&tp);   
srand(static_cast<unsigned int>(getpid()) ^ 
static_cast<unsigned int>(pthread_self()) ^ 
static_cast<unsigned int >(tp.millitm));

For better random quality, use /dev/urandom. You can make the above code portable in using boost::thread and boost::date_time.

Edouard A.