在POSIX上产生随机双精度的最佳方法是什么?
我想获得[0.0,1.0)范围内的均匀分布
如果可能的话,请让实现使用来自/ dev / urandom的随机字节。
如果解决方案是线程安全的,那也很好。如果不确定,请指出。
阅读其他答案后,请参阅我考虑过的一些解决方案。
解决方案
#include <stdlib.h> printf("%f\n", drand48());
/ dev / random:
double c; fd = open("/dev/random", O_RDONLY); unsigned int a, b; read(fd, &a, sizeof(a)); read(fd, &b, sizeof(b)); if (a > b) c = fabs((double)b / (double)a); else c = fabs((double)a / (double)b);
c是随机值
从文件读取是线程安全的AFAIK,因此使用fopen()从/ dev / urandom读取将产生"真正随机"的字节。
尽管可能存在陷阱,但是将这样一组字节访问为整数(除以该大小的最大整数)的方法将产生一个介于0到1之间且近似于该分布的浮点值。
例如:
FILE* f = fopen("/dev/urandom", "r"); int32_t int; fread(&int, sizeof(int32_t), 1, f); fclose(f); double theRandomValue = int / (double) (2 ** 32 - 1);
诀窍是我们需要一个符合我们要求的54位随机数发生器。几行代码加上一个联合,将这54位粘贴在尾数上,我们便有了自己的号码。诀窍不是"双浮点数",而是我们所需的随机数。
简单:假设采用IEEE,则double具有52位精度。因此,生成一个52位(或者更大)的无符号随机整数(例如,通过从dev / urandom中读取字节),将其转换为double并将其除以2 ^(原来的位数)。
这给出了数值均匀的分布(因为值在给定范围内的概率与该范围成正比)低至第52个二进制数位。
复杂:但是,在上述[0,1)范围内,有很多双精度值无法生成。具体来说,范围[0,0.5)(设置了最低有效位的值)的一半值不会出现。 [0,0.25)范围内的值的四分之三(设置了至少2位的值)不会出现,依此类推,一直到只有一个小于2 ^ -51的正值是可能的,尽管有两倍能够代表这样的价值。因此,不能说它在指定范围内达到完全精确的统一。
当然,我们不希望以相等的概率选择其中一个双精度数,因为这样得出的平均数将太小。我们仍然需要结果在给定范围内的概率与该范围成正比,但是在适用范围上具有更高的精度。
我认为以下作品。我还没有特别研究或者测试过该算法(就像我们可能通过没有代码的方式可以看到的那样),而且就我个人而言,如果没有找到表明其有效的正确引用,我就不会使用它。但是这里:
- 从52开始计算指数,然后选择一个52位随机无符号整数(假设尾数为52位)。
- 如果整数的最高有效位为0,则将指数增加1,将整数左移1,然后用新的随机位填充最低有效位。
- 重复该操作,直到我们在最高有效位达到1,否则该指数对于双倍来说太大(1023. 或者可能是1022)。
- 如果找到1,则将值除以2 ^指数。如果全为零,则返回0(我知道,这实际上不是特例,但要强调的是,0返回的可能性很小[编辑:实际上,这可能是特例-这取决于我们是否要生成如果不是这样,那么一旦连续有足够的0,就丢弃所有剩余的内容并返回0。但是实际上,除非随机源不是随机的,否则这几乎不可能忽略不计。
请注意,我不知道这种随机双打是否实际上有任何实际用途。我们对随机的定义应在某种程度上取决于其用途。但是,如果我们可以从随机的52个有效位中受益,那么这实际上可能会有所帮助。
这似乎是一个很好的方法:
`
unsigned short int r1, r2, r3; // let r1, r2 and r3 hold random values double result = ldexp(r1, -48) + ldexp(r2, -32) + ldexp(r3, -16);
`
这基于NetBSD的drand48实现。
/ dev / urandom不是POSIX,并且通常不可用。
在[0,1)中均匀生成一个双精度值的标准方法是生成一个在[0,2 ^ N)范围内的整数并除以2 ^ N。因此,选择我们喜欢的随机数生成器并使用它。对于模拟,我的是Mersenne Twister,因为它速度极快,但关联性仍然不高。实际上,它可以为我们做到这一点,甚至具有一个版本,可以为较小的数字提供更高的精度。通常,我们先给它提供种子,这有助于调试或者向其他人显示结果的可重复性。当然,如果未指定代码,我们可以让代码从/ dev / urandom中获取一个随机数作为种子。
出于加密目的,我们应该改用其中一种标准加密库,例如openssl),该库在可用时的确会使用/ dev / urandom。
至于线程安全性,至少在标准接口中不会,因此我们需要在其上构建一层,或者仅在一个线程中使用它们。线程安全的线程提供了它们修改的状态,因此,我们可以有效地运行多个非交互的随机数生成器,而这些生成器可能不是我们想要的。