我正在尝试在0和1之间生成一个随机浮点数(无论是[0,1]还是[0,1],对我来说都不重要)。关于此问题的在线问题似乎涉及rand()
调用,种子为time(NULL)
,但我希望能够每秒多次调用我的程序并且每次都获得不同的随机数。这引导我进入Linux中的getrandom系统调用,它来自/ dev / urandom。我想出了这个:
#include <stdio.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <stdint.h>
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()的例子。
答案 0 :(得分:8)
如何非常随机地启动序列。
如何在[0 ... 1)范围内生成double
。
通常的方法是采用非常随机的来源,例如/dev/urandom
或来自syscall()
或甚至seed = time() ^ process_id;
的结果,并通过srand()
播种。然后根据需要致电rand()
。
下面包含一个快速转换的方法来生成统一[0.0 to 1.0)
(线性分布)。但是像所有随机生成函数一样,真正好的函数基于广泛的研究。这个只需根据rand()
和DBL_MANT_DIG
调用RAND_MAX
几次,
[编辑]原始double rand_01(void)
有一个缺点,即它只生成2 ^ 52个不同的double
而不是2 ^ 53。它已被修改。替代方案:远远低于double
的{{1}}版本。
rand_01_ld(void)
如果想要扩展到更宽的FP,则无符号宽整数类型可能不足。以下是一种没有这种限制的便携式方法。
#include <assert.h>
#include <float.h>
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
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;
}
答案 1 :(得分:5)
如果您需要生成双打,可以使用以下算法:
CPython generates random numbers使用以下算法( I 更改了函数名,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中对此功能的评论注意到以下内容:
[this function]是原始代码中名为genrand_res53的函数; 在[0,1]上生成一个53位分辨率的随机数;注意
9007199254740992 == 2**53
;我假设他们拼写为“/2**53
” 在编译器所希望的(可能是徒劳的)希望中相乘 在编译时优化分部。67108864
是2**26
。在 效果,a包含27个随机位,左移26,b
填充 低于此位的53位分子的26位。原始代码将Isaku Wada归功于此算法,2002/01/09
从该代码中简化,如果您想快速创建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,然后从那里开始。