我是C编程的新手,我已经读到erand48((是线程安全随机数生成的一个不错的选择。但是,该函数的种子值为: 无符号短 int 数组[3]
关于此种子值应初始化为什么的任何建议?
好吧。因此,首先,让我明确指出,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
定期将熵注入并刷新,确保您拥有相当随机的数字的可观供应。
只有我的 2 美分...
前段时间,我们在其中一个项目中使用了相同的函数系列。该产品必须在Windows/Unix上运行,所以我们没有使用/dev/random。
相反,我们将 host_ip_addr + time(( + process_id + thread_id 连接为字符串,并从该字符串生成一个哈希。然后,我们将尾随字节 XOR 分散到 6 个前导字节上。我们使用最终数据作为种子...
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(。你可以用任何东西来播种它 - 只需获得 48 个随机位并将它们推入数组[3],来自/dev/urandom,来自 time/pid/whatever。
几个陷阱:- lcong48(( - 忘记它甚至存在- "我如何确保两个种子不会彼此靠近得离谱?"- 不要,只使用另一个 RNG 会花费更少的时间。