使用getrandom实现C中的随机浮点
我试图生成一个介于0和1之间的随机浮点数(无论它是在[0,1]还是[0,1]上,对我来说都不重要)。在线上关于这一点的每一个问题似乎都涉及到使用getrandom实现C中的随机浮点,c,random,floating-point,C,Random,Floating Point,我试图生成一个介于0和1之间的随机浮点数(无论它是在[0,1]还是[0,1]上,对我来说都不重要)。在线上关于这一点的每一个问题似乎都涉及到rand()调用,以时间(NULL)为种子,但我希望能够每秒调用我的程序不止一次,每次都能得到不同的随机数。这让我想到了Linux中的getrandom系统调用,它来自/dev/uradom。我想到了这个: #include <stdio.h> #include <sys/syscall.h> #include <unistd.
rand()
调用,以时间(NULL)为种子
,但我希望能够每秒调用我的程序不止一次,每次都能得到不同的随机数。这让我想到了Linux中的getrandom系统调用,它来自/dev/uradom。我想到了这个:
#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;
}
#包括
#包括
#包括
#包括
int main(){
uint32_t r=0;
对于(int i=0;i<20;i++){
syscall(SYS\u getrandom,&r,sizeof(uint32\u t),0);
printf(“%f\n”,((双)r)/UINT32_MAX);
}
返回0;
}
我的问题只是我是否正确地执行了此操作。它似乎有效,但我担心我误用了某些东西,而且几乎没有使用getrandom()online的示例。如果需要生成双精度,可以使用以下算法: CPython使用以下算法(我更改了函数名、typedefs和返回值,但算法保持不变): 该算法的源代码是西村拓二和松本Makoto的Mersenne Twister 19937随机数生成器。不幸的是,源代码中提到的原始链接不再可供下载 CPython中关于此函数的注释说明如下: [此函数]是原始代码中名为genrand_res53的函数; 在[0,1]上生成一个53位分辨率的随机数;注意
9007199254740992==2**53
;我假设他们将“/2**53
”拼写为
乘以倒数(可能是徒劳的),希望编译器
在编译时优化除法。67108864
是2**26
效果是,a包含27个左移26位的随机位,b
填充
53位分子的下26位
该算法的原始代码为伊萨库·瓦达(Isaku Wada),2002/01/09
从该代码简化,如果要创建
浮点
快速,应使用(1OP有两个问题:
如何非常随机地开始序列
如何在[0…1)范围内生成一个double
通常的方法是获取一个非常随机的源,比如/dev/uradom
,或者从syscall()
或者甚至可能seed=time()^process\u id;
通过srand()
进行seed。然后根据需要调用rand()
下面包含了一个快速转换的方法来生成一个统一的[0.0到1.0)
(线性分布)。但是像所有的随机生成函数一样,真正好的生成函数是基于广泛的研究的。这个函数只是基于DBL\u MANT\u DIG
和rand\u MAX
调用几次rand()
[编辑]原始的double rand\u 01(void)
有一个缺点,它只生成2^52个不同的double
s,而不是2^53。它已被修改。备选方案:一个double
版本的rand\u 01\u ld(void)
如下所示
#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;
}
#包括
#包括
#包括
#包括
#包括
#包括
#包括
双随机变量01(无效){
assert(FLT_RADIX==2);//DBL_MANT_DIG需要
unsigned long long limit=(1ll而不是syscall()
,您是否可以打开fopen('/dev/uradom',rb')
并读取4个字节?或者将其放入srand()
?一种更方便的方法是打开/dev/uradom
并读取(2)
来自它。@user3030010,是什么让你认为系统调用比从Uradom设备读取更不容易失败?如果用随机位填充double或float,你可以生成NaN或无穷大,但没有人建议这样做。如果生成一个填充了随机位的无符号整数,请转换为double
,然后除以最大可能值,则在闭合间隔[0,1]内可靠地得到一个双精度@tofro S/他说的不是重复播种,而是重复运行程序,程序在启动时播种一次。我在想,从更多随机位开始比结果有尾数位对精度的影响。为什么不在合适的整数中生成DBL_尾数_位
随机位,将其转换为double
,并使用ldexp()
将其缩放到范围[0,1)
?@John Bollinger挑战在于“生成DBL\u尾数\u位随机位”在RAND\u MAX
和DBL\u MANT\u DIG
方面以可移植的方式。当然希望代码在编译时知道log2(RAND\u MAX+1)
的整数常量,而不存在任何范围错误或其他问题。即使RAND\u MAX+1u
也可能溢出。任何想法都值得赞赏。@John Bollinger添加了第二种方法。@(对于长双精度
)不使用额外的随机位。很好。如果可以的话,我会再给你一个+1。另一篇信息性文章。有什么反对float.h
和limits.h
的吗?
#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;
}
#include <math.h>
result = ldexpf(r & ((1 << FLT_MANT_DIG) - 1), -FLT_MANT_DIG);
result = ldexpf(r % (1 << FLT_MANT_DIG), -FLT_MANT_DIG);
#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;
}
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;
}