tags:

views:

646

answers:

5

I'm using srandom() and random() to generate random numbers in c on a Unix system. I would like to have multiple RNGs. Each one, given the same seed, should output the same sequence. I would also like to save and restore the state of each one. Here's a pseudocode example:

R1 = new_rng(5); //5 is the seed
R2 = new rng(5); //5 is the seed here, too.
a = R1.random();
b = R1.random();
d = R2.random(); //a == d
s1 = R2.get_state(); //save the state of R2
e = R2.random(); //b == e
R2.set_state(s1); //restore the state of R2
f = R2.random(); //b == f

How do I do this? Sometimes the RNGs will fork into different threads and I need to replicate the state of the RNG when creating a new thread, too.

A: 

I'm not sure you can rely on your PRNG to produce the exact same sequence given an identical seed actually. I know that some methods work that way, but I believe that some of the better ones include a certain amount of non-determinism, so that an identical seed may lead to a different sequence. You'll have to go through your libc documentation with a fine-toothed comb and see if this is mentioned somewhere. If not, check the code (if you're so lucky to have access to the code).

At any rate, this will couple your application very tightly indeed to the PRNG implementation in your libc. You'll definitely be bound to the flavour of libc you're developing on, and maybe even libc version. If this feature is very important, you may have to reimplement random number generation in your app to ensure portability and reproducability.

arnsholt
No way. The PNRG must reproduce the same sequence each time. How else would we repeat tests for debugging. A PNRG that doesn't is broken.
Eyal
+1  A: 

Read this article on Mersenne twister. There are links to several implementations at the very bottom. (In other words, implement a PRNG yourself.)

avakar
+1  A: 

there are some C library extension on various UNIX flavours:

  • BSD like random, check initstate/setstate
  • _r variants (random_r, srandom_r, initstate_r, etc)
  • rand_r (stdlib.h)

which flavour is supported by your target UNIX?

dfa
+1  A: 

Use rand_r(unsigned *seed) instead of srand() and rand(). That way you can maintain multiple random seeds.

laalto
See the other answer that says rand_r() shouldn't be used (and gives reasons)
user9876
+3  A: 

Use erand48()/nrand48()/jrand48() to generate double precision floating point, non-negative long integer, or signed long integer random numbers, respectively. These functions allow you to have as many independent sequences as desired; the state is passed in as an argument and can easily be saved and restored. Furthermore, the sequence is defined by the standard and will not vary across runs, even on different platforms.

Some other answers suggest rand_r(). This function is obsolescent in POSIX.1-2008, which contains this note:

The drand48() function provides a much more elaborate random number generator.

The limitations on the amount of state that can be carried between one function call and another mean the rand_r() function can never be implemented in a way which satisfies all of the requirements on a pseudo-random number generator. Therefore this function should be avoided whenever non-trivial requirements (including safety) have to be fulfilled.

The rand_r() function may be removed in a future version.

mark4o
drand48() is this:new random number = old random number * a + cSeems really wimpy! Surely random() was stronger than that.
Eyal
No, not really. Many "random" implementations are similar multiply-with-carry generators, or similarly simple LFSRs.
ephemient
Actually it is new_state = old_state * a + c; the state and arithmetic is 48 bits and the result is taken from the high bits of the state. If you want strength then you should be using a cryptographically secure random number generator. Usually that will be slower and take more storage to save/restore. The OP was more interested in repeatability and save/restore capability.
mark4o