【问题标题】:What seed value should I use for erand48() in C?我应该为 C 中的 erand48() 使用什么种子值?
【发布时间】:2014-10-13 20:42:31
【问题描述】:

我是 C 编程的新手,我读到 erand48() 是线程安全随机数生成的一个不错的选择。但是,该函数采用以下种子值: unsigned short int array[3]

关于这个种子值应该被初始化成什么的任何建议?

【问题讨论】:

  • 为什么和srand不一样:time?由于您还有更多的内容要填写,所以也可以输入pid
  • @Jongware 因为time 没有更新到足以导致线程之间存在差异的任何地方(libc:time 为 1s,pthread:create
  • @haneef:谢谢!我完全忘记了线程,实际上很可能(如果不是完全通常)将在完全相同的毫秒内创建多个线程。

标签: c random concurrency


【解决方案1】:

好的。所以首先,让我明确一点,libc 中的 PRNG 是确定性的(这就是它想要种子的原因),使用LCG - 这意味着一旦你有几个值就很容易预测所有值,因此不安全。

那么现在。 erand48() 从伪随机实数的均匀分布中返回大小为 double 的随机浮点值。它本身不需要种子值,而是需要您提供状态缓冲区。这是完整的声明:

double erand48(unsigned short xsubi[3]);

有趣的是,状态缓冲区必须以随机值作为种子,以使生成器工作。我的第一个想法是阅读/dev/urandom

我们可以用这样的方法来做到这一点(使用无缓冲读取来防止这种小读取造成的浪费):

#include <stdio.h>
#include <stdlib.h>

void *thread_f (void *i) {
    // setup unbuffered urandom
    urandom = fopen ("/dev/urandom", "r");
    setvbuf (urandom, NULL, _IONBF, 0);  // turn off buffering

    // setup state buffer
    unsigned short randstate[3];
    // fgetc() returns a `char`, we need to fill a `short`
    randstate[0] = (fgetc (urandom) << 8) | fgetc (urandom);
    randstate[1] = (fgetc (urandom) << 8) | fgetc (urandom);
    randstate[2] = (fgetc (urandom) << 8) | fgetc (urandom);


    // cleanup urandom
    fclose (urandom);

    // you can now use erand48 (randstate);

    ...     // do whatever work you need to do

    return result;
}

这是线程安全的,甚至可以确保所有线程的种子值相对安全。

当然,如果速度不是太大的问题(即:你可以忍受一点速度损失)并且你可以忍受整数,那么直接从/dev/urandom 进行无缓冲读取是一个完美的解决方案。更好的是,/dev/urandom 提供了安全的、不可预测的伪随机整数(从技术上讲,是一个字节流,但只要您匹配大小,它们将始终作为整数工作)通常也是均匀分布的。

另外,/dev/urandom 会定期向其中注入熵并刷新,确保您拥有相当数量的相当随机数。

【讨论】:

  • IO 函数是线程安全的(这就是为什么你有 getc_unlocked 和朋友); (nitpick-on) fgetc 返回一个字节,你需要两个,LCG 链接指向 LFSR (nitpick-off)
  • @loreb 我不知道为什么我认为他们不是。无论如何,修复 LCG 和fgetc()。感谢您指出!
【解决方案2】:

只要我的 2 美分...

不久前,我们在一个项目中使用了相同的函数系列。该产品必须在 Windows/Unix 上运行,所以我们没有使用 /dev/random

相反,我们将 host_ip_addr + time() + process_id + thread_id 连接为字符串,并从该字符串生成哈希。然后,我们将异或的尾随字节传播到 6 个前导字节上。我们将最终数据用作我们的种子...

【讨论】:

    【解决方案3】:

    rand48 系列是 48 位 LCG,意思类似于

    double erand48(uint48_t *state) {
        *state = *state * A + C;             /* A and C are suitable values */
        return *state / 281474976710656.0;   /* that's 2^48 */
    }
    

    ...除了我从未见过具有 uint48_t 的计算机,因此您传递的是一个具有 48 位状态的数组(假设一个短有 16 位,则为 3x16)。 您可以用任何东西播种它 - 只需从 /dev/urandom 和 time/pid/whatever 获取 48 个随机位并将它们推入数组 [3]。

    几个问题: - lcong48() - 忘记它甚至存在 - “我如何确保两颗种子不会离得太近?” - 不要,使用另一个 rng 会花费更少的时间。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-11-10
      • 1970-01-01
      • 1970-01-01
      • 2016-06-19
      • 2012-12-16
      • 2017-07-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多