使用getrandom在C中随机浮动

我试图在0和1之间生成一个随机浮点数(无论是[0,1]还是[0,1])对我来说都不重要。 关于这个的每个问题似乎涉及rand()调用,播种time(NULL) ,但我希望能够每秒多次调用我的程序并且每次都得到不同的随机数。 这引导我进入Linux中的getrandom系统调用,它来自/ dev / urandom。 我想出了这个:

 #include  #include  #include  #include  int main() { uint32_t r = 0; for (int i = 0; i < 20; i++) { syscall(SYS_getrandom, &r, sizeof(uint32_t), 0); printf("%f\n", ((double)r)/UINT32_MAX); } return 0; } 

我的问题是我是否正确地这样做。 它似乎有效,但我担心我会误用某些东西,并且接下来没有使用getrandom()在线的例子。

OP有2个问题:

  1. 如何非常随机地启动序列。

  2. 如何在[0 … 1)范围内生成double

通常的方法是采用非常随机的源,如/dev/urandomsyscall()的结果,甚至可能是seed = time() ^ process_id; 和种子通过srand() 。 然后根据需要调用rand()

下面包括一个快速转换的方法来生成均匀[0.0 to 1.0) (线性分布)。 但是像所有随机生成函数一样,真正好的函数基于广泛的研究。 这个简单地根据DBL_MANT_DIGRAND_MAX调用rand()几次,

[编辑]原始double rand_01(void)有一个弱点,它只生成2 ^ 52个不同的double s而不是2 ^ 53。 它已被修改。 替代方案:远远低于rand_01_ld(void)double版本。

 #include  #include  #include  #include  #include  #include  #include  double rand_01(void) { assert(FLT_RADIX == 2); // needed for DBL_MANT_DIG unsigned long long limit = (1ull << DBL_MANT_DIG) - 1; double r = 0.0; do { r += rand(); // Assume RAND_MAX is a power-of-2 - 1 r /= (RAND_MAX/2 + 1)*2.0; limit = limit / (RAND_MAX/2 + 1) / 2; } while (limit); // Use only DBL_MANT_DIG (53) bits of precision. if (r < 0.5) { volatile double sum = 0.5 + r; r = sum - 0.5; } return r; } int main(void) { FILE *istream = fopen("/dev/urandom", "rb"); assert(istream); unsigned long seed = 0; for (unsigned i = 0; i < sizeof seed; i++) { seed *= (UCHAR_MAX + 1); int ch = fgetc(istream); assert(ch != EOF); seed += (unsigned) ch; } fclose(istream); srand(seed); for (int i=0; i<20; i++) { printf("%f\n", rand_01()); } return 0; } 

如果想要扩展到更宽的FP,则无符号宽整数类​​型可能不足。 下面是一个没有这种限制的便携式方法。

 long double rand_01_ld(void) { // These should be calculated once rather than each function call // Leave that as a separate implementation problem // Assume RAND_MAX is power-of-2 - 1 assert((RAND_MAX & (RAND_MAX + 1U)) == 0); double rand_max_p1 = (RAND_MAX/2 + 1)*2.0; unsigned BitsPerRand = (unsigned) round(log2(rand_max_p1)); assert(FLT_RADIX != 10); unsigned BitsPerFP = (unsigned) round(log2(FLT_RADIX)*LDBL_MANT_DIG); long double r = 0.0; unsigned i; for (i = BitsPerFP; i >= BitsPerRand; i -= BitsPerRand) { r += rand(); r /= rand_max_p1; } if (i) { r += rand() % (1 << i); r /= 1 << i; } return r; } 

如果需要生成双精度数,可以使用以下算法:

CPython使用以下算法生成随机数 ( 更改了函数名,typedef和返回值,但算法保持不变):

 double get_random_double() { uint32_t a = get_random_uint32_t() >> 5; uint32_t b = get_random_uint32_t() >> 6; return (a * 67108864.0 + b) * (1.0 / 9007199254740992.0); } 

该算法的来源是Takuji Nishimura和Makoto Matsumoto的Mersenne Twister 19937随机数发生器。 不幸的是,源中提到的原始链接不再可供下载。

CPython中对此function的评论注意到以下内容:

[this function]是原始代码中名为genrand_res53的函数; 在[0,1]上生成一个53位分辨率的随机数; 注意9007199254740992 == 2**53 ; 我假设它们拼写为“ /2**53 ”,因为编译器会在编译时优化除法,这可能是徒劳的。 671088642**26 。 实际上,a包含27个随机位向左移位26, b填充53位分子的低26位。

原始代码将Isaku Wada归功于此算法,2002/01/09


从该代码中简化,如果要快速创建float ,则应使用(1 << FLT_MANT_DIG) - 1屏蔽uint32_t的位并除以(1 << FLT_MANT_DIG)以获得正确的[0, 1)间隔:

 #include  #include  #include  #include  #include  int main() { uint32_t r = 0; float result; for (int i = 0; i < 20; i++) { syscall(SYS_getrandom, &r, sizeof(uint32_t), 0); result = (float)(r & ((1 << FLT_MANT_DIG) - 1)) / (1 << FLT_MANT_DIG); printf("%f\n", result); } return 0; } 

由于可以假设你的Linux有一个C99编译器,我们可以使用ldexpf代替那个分区:

 #include  result = ldexpf(r & ((1 << FLT_MANT_DIG) - 1), -FLT_MANT_DIG); 

要获得闭合间隔[0, 1] ,您可以稍微降低效率

 result = ldexpf(r % (1 << FLT_MANT_DIG), -FLT_MANT_DIG); 

为了快速生成大量高质量的随机数,我只需使用系统调用来获取足够的数据来为PRNG或CPRNG播种,然后从那里开始。