【问题标题】:C random() & setstate function not behaving as expectedC random() & setstate 函数未按预期运行
【发布时间】:2012-12-06 10:11:50
【问题描述】:

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

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

对比

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

【问题讨论】:

  • C 只有randsrand 并且没有randominitstatesetstate。请用您的操作系统标记您的问题。
  • @JensGustedt 我添加了 bsd 标签。
  • 使用gcc (Debian 4.4.5-8) 4.4.5在Debian(稳定版)上运行这个例子,我得到了所有三个代码片段的相同两个数字。
  • 所以看起来这是一个错误/不正确的实现

标签: c random bsd


【解决方案1】:

两种实现都是正确的。

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

Initstate 可以做同样的事情,但也可以先改变缓冲区的内容。如果 PRNG 类似于 ARC4 或 Spritz,则缓冲区需要是一个排列,而不仅仅是任意位。如果 PRNG 是非线性加性反馈生成器,则至少需要设置状态中某处的低位之一,否则它将无法正常工作。并且一些库对状态缓冲区进行哈希处理,因此仅从种子+输出信息中就不容易判断正在使用哪种 PRNG。他们不需要;如果它使用的生成器是 LFSG 或不需要任何特定格式或对缓冲区有任何一致性需求的东西,lib 可以对 initstate 和 setstate 做完全相同的事情。但是,如果您不执行 initstate,并且您的操作系统使用具有此类需求的东西,那么您的可重复序列可能不会像您希望的那样不可预测。

【讨论】:

    【解决方案2】:

    我猜对initstate() 的调用不会也切换到该状态,但对setstate() 的调用会,这就是为什么后者的random() 调用返回一个从新状态生成的数字。

    【讨论】:

    • 对代码进行了一些实验,我认为情况并非如此。在initstate() 之后添加对setstate() 的调用没有任何区别。
    【解决方案3】:

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

    The comment for setstate 表示使用当前缓冲区调用它是可以的,所以要么这是预期的行为,要么是 decades old 错误。

    注意,由于事情的执行顺序,调用是可以的 setstate() 与当前状态相同。

    【讨论】:

      【解决方案4】:

      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,并得到相同的随机数序列。 请注意,在您复制到应该用于随机数的缓冲区或从缓冲区复制之前,您必须使用 setstate()initstate() 告诉 random() 停止使用该缓冲区。原因是当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());
      

      【讨论】:

        猜你喜欢
        • 2020-06-02
        • 2011-10-31
        • 2023-03-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-04-14
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多