C random()& setstate函数的行为不符合预期

时间:2012-12-06 10:11:50

标签: c random bsd

我不确定为什么这两个代码块会给出不同的输出:

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

我误解了setstate()的作用吗?

编辑: 有趣的是,看看它给出了什么:

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

4 个答案:

答案 0 :(得分:3)

两种实现都是正确的。

Setstate只是更改例程中的静态指针以指向缓冲区。

允许Initstate执行相同的操作,但也允许首先更改缓冲区内容。如果PRNG类似于ARC4或Spritz,则缓冲区需要是排列而不是任意位。如果PRNG是非线性附加反馈发生器,那么状态中某处的低位中的至少一个需要被设置或者它不能正常工作。并且一些库对状态缓冲区进行散列,因此很容易从种子+输出信息中分辨出正在使用的PRNG类型。他们并不需要;如果它使用的生成器是LFSG或者不需要任何特定格式或者对缓冲区有任何一致性需求的东西,则lib可以为initstate和setstate做同样的事情。但是如果你不做initstate,并且你的操作系统使用了具有这种需求的东西,那么你的可重复序列可能不像你想要的那样难以预测。

答案 1 :(得分:2)

我想对initstate()的调用也不会切换到该状态,但调用setstate()会这样做,这就是为什么后一个random()调用会返回一个从新州。

答案 2 :(得分:2)

setstate的BSD实现在将其存储到旧缓冲区之前加载辅助状态信息,以进行错误检查。此外,initstatesetstate是更新此信息的唯一功能。这意味着当使用相同的缓冲区时loads stale statestores the new dataupdates the internal state with the former。以这种方式重复调用setstate将交替使用旧的存储状态和当前内部状态,从而在调用两次时观察到结果。

The comment for setstate表示使用当前缓冲区调用它是正常的,因此这是预期的行为或decades old错误。

  

请注意,由于事情的顺序,可以打电话   setstate()具有与当前状态相同的状态。

答案 3 :(得分:1)

initstate()随机告诉用于存储信息的缓冲区 下一个随机数。您在调用random()时会得到不同的答案,因为缓冲区state1[256]中的信息已更改。查看以下代码的输出:

#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());
}

这给出了输出:

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

您可以看到,在第一次调用random()后,缓冲区state1的内容发生了变化。 random()使用该区域来存储其状态。此状态将复制到缓冲区tmp。稍后我们将其复制回state1,并获得相同的随机数序列。 请注意,在复制到应该用于随机数的缓冲区之前或之后,您必须告诉random()使用setstate()initstate()停止使用该缓冲区。原因是当调用setstate()时,会修改旧缓冲区以允许使用setstate()再次加载它。

因此,要获得与原始问题相同的答案,您必须使用:

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());