5

I am unsure why these two blocks of code give different outputs:

unsigned int seed1 = 0;
char state1[256];
initstate(seed1, state1, 256);
printf("%10ld\n", random());
printf("%10ld\n", random());
// Gives:
// 1216130483
// 1950449197

vs.

unsigned int seed1 = 0;
char state1[256];
initstate(seed1, state1, 256);
printf("%10ld\n", random());
setstate(state1);
printf("%10ld\n", random());
// Gives:
// 1216130483
// 625602885

Am I misunderstanding what setstate() does?

EDIT: Interestingly enough, look at what this gives:

unsigned int seed1 = 0;
char state1[256];
initstate(seed1, state1, 256);
printf("%10ld\n", random());
setstate(state1);
setstate(state1);
printf("%10ld\n", random());
// Gives:
// 1216130483
// 1950449197
David Lawson
  • 7,802
  • 4
  • 31
  • 37

4 Answers4

3

Both implementations are correct.

Setstate just changes a static pointer in the routine to point at your buffer.

Initstate is allowed to do the same thing, but is also allowed to alter the buffer contents first. If the PRNG is something like ARC4 or Spritz, the buffer needs to be a permutation rather than just arbitrary bits. If the PRNG is a nonlinear additive feedback generator, then at least one of the low bits somewhere in the state needs to be set or it won't work right. And some libs hash the state buffer so it won't be easy to tell what kind of PRNG is in use just from the seed+output information. They aren't required to; the lib can do exactly the same thing for initstate and setstate if the generator it's using is an LFSG or something that doesn't require any particular format or have any consistency needs for the buffer. But if you don't do initstate, and your OS uses something that has such needs, your repeatable sequence may not be as unpredictable as you'd like.

Ray
  • 31
  • 1
2

I guess the call to initstate() doesn't also switch to that state, but the call to setstate() does, which is why the latter random() call returns a number generated from the new state.

unwind
  • 391,730
  • 64
  • 469
  • 606
  • 2
    Having experimented with the code a little, I don't think this is the case. Adding a call to `setstate()` right after `initstate()` makes no difference whatsoever. – NPE Dec 06 '12 at 10:19
2

The BSD implementation of setstate loads auxiliary state information, for error checking purposes, before storing it to the old buffer. Additionally initstate and setstate are the only functions that update this information. This means that when the same buffer is used it loads stale state, stores the new data, and updates the internal state with the former. Calling setstate in this manner repeatedly will alternate the old stored state, and the current internal state, causing the results observed when called twice.

The comment for setstate says that calling it with the current buffer is OK, so either this is the intended behavior, or a decades old bug.

Note that due to the order in which things are done, it is OK to call setstate() with the same state as the current state.

kdhp
  • 2,096
  • 14
  • 15
1

initstate() tells random what buffer to use to store information for the next random number. You get different answers in calls to random() because the information in your buffer state1[256] has changed. Look at the output of the following code:

#define LEN (32)
void print_hex( char *b)
{
   int i;
   for( i=0; i < LEN; i++) printf("%02x ",((unsigned char *)b)[i]);     
   printf("\n");
}

main()
{
   char state1[256], state2[256], tmp[256];
   initstate( 42, state2, LEN);
   initstate( 62, state1, LEN) ;
   printf("buffer before random():\n");
   print_hex( state1 ) ;
   printf("%10ld\n", random());
   printf("buffer after random():\n");
   print_hex( state1 ) ;

   setstate( state2 ); // Now we are free to copy data from state1
   printf("buffer after setstate():\n");
   print_hex( state1 ) ;
   memcpy( tmp, state1, 256);
   printf("copied to tmp\n");

   setstate( state1 ); // Go on with original sequence
   printf("next random():\n") ;
   printf("%10ld\n", random());
   printf("next random():\n") ;
   printf("%10ld\n", random());

   setstate( state2 ) ; // Again, this allows us to play with data in state1
   memcpy( state1, tmp, 256);
   setstate( state1 ) ;
   printf("back copy:\n");
   printf("random() after copy:\n") ;
   printf("%10ld\n", random());
   printf("next random():\n") ;
   printf("%10ld\n", random());
}

This gives the output:

buffer before random():
01 00 00 00 e7 22 1d 21 f1 62 9c 90 89 72 b5 89 35 2b 97 b5 76 8c ff a8 56 14 14 7b ba 19 d9 f7
1801070350
buffer after random():
01 00 00 00 e7 22 1d 21 f1 62 9c 90 89 72 b5 89 1c 4e b4 d6 76 8c ff a8 56 14 14 7b ba 19 d9 f7
buffer after setstate():
06 00 00 00 e7 22 1d 21 f1 62 9c 90 89 72 b5 89 1c 4e b4 d6 76 8c ff a8 56 14 14 7b ba 19 d9 f7
copied to tmp
next random():
 483260339
next random():
  40158063
back copy:
random() after copy:
 483260339
next random():
 40158063

You can see that after the first call to random() the contents of buffer state1 changes. random() uses that area to store its state. This state is copied to buffer tmp. Later we copy it back to state1, and get the same sequence of random numbers. Note that before you copy to or from a buffer that is supposed to be used for random numbers you have to tell random() to stop using that buffer using setstate() or initstate(). The reason is that when setstate() is called, the old buffer is modified to allow it to be loaded again with setstate().

So to get the same answer as in the original question, you have to use:

unsigned int seed1 = 42;
char state1[256], tmp[256];
initstate(seed1, state1, 256);
printf("%10ld\n", random());
initstate( 0, tmp, 256); // <- notice this
setstate( state1 ) ;
printf("%10ld\n", random());