如果您需要生成双精度浮点数,以下算法可能会有用:
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中,关于这个函数的注释指出:
[此函数] 是原始代码中名为genrand_res53的函数;生成一个具有53位分辨率的[0,1)之间的随机数;请注意,9007199254740992 == 2 ** 53;我假设他们将“/2**53”拼写成乘以倒数的形式,以便编译器可以在编译时优化掉除法。67108864等于2 ** 26。实际上,a包含了27个左移26位的随机位,而b填充53位分子的较低的26位。
原始代码将Isaku Wada归功于这个算法,时间是2002年1月9日。
简化代码后,如果你想快速创建一个
float
,你应该使用
uint32_t
的位掩码和
(1 << FLT_MANT_DIG) - 1
除以
(1 << FLT_MANT_DIG)
来获得正确的
[0, 1)
区间。
#include <stdio.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <stdint.h>
#include <float.h>
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 <math.h>
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,然后从那里继续。
fopen('/dev/urandom', 'rb')
打开文件并读取4个字节,而不是使用syscall()
函数。然后将这4个字节传递给srand()
函数。这样做可以实现同样的随机数生成效果。 - chux - Reinstate Monica/dev/urandom
并从中读取read(2)
。 - Nate Eldredgedouble
,然后除以最大可能值,那么你可靠地得到一个在闭区间[0,1]内的double
。 - John Bollinger