在POSIX上生成随机双精度的最佳方法是什么?

时间:2008-09-28 18:09:50

标签: c++ multithreading random posix

我想在范围[0.0,1.0)

中获得均匀分布

如果可能,请让实现使用 / dev / urandom中的随机字节。

如果您的解决方案线程安全,那也很好。如果您不确定,请说明。

在阅读其他答案后,请参阅some solution

6 个答案:

答案 0 :(得分:3)

简单:假设IEEE,双精度具有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个有效位中获益,那么这实际上可能会有所帮助。

答案 1 :(得分:3)

这似乎是非常好的方式:

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实现。

答案 2 :(得分:0)

#include <stdlib.h>
printf("%f\n", drand48());

的/ dev /随机:

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是你的随机值

答案 3 :(得分:0)

从文件中读取是线程安全的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);

答案 4 :(得分:0)

诀窍是你需要一个符合你要求的54位随机数发生器。几行代码用一个联合来粘贴尾数中的54位,你有你的号码。诀窍不是双重浮动技巧是你想要的随机发生器。

答案 5 :(得分:0)

/ dev / urandom不是POSIX,通常不可用。

在[0,1]中均匀生成double的标准方法是生成[0,2 ^ N]范围内的整数并除以2 ^ N.因此,选择您最喜欢的随机数生成器并使用它。对于模拟,我的是Mersenne Twister,因为它非常快,但仍然没有很好的相关性。实际上,它可以为你做到这一点,甚至有一个版本可以为较小的数字提供更高的精度。通常,您可以给它一个种子,这有助于重复调试或向其他人展示您的结果。当然,如果没有指定,你可以让代码从/ dev / urandom中获取一个随机数作为种子。

出于加密目的,您应该使用其中一个标准加密库,例如openssl),它们确实会在可用时使用/ dev / urandom。

至于线程安全,大多数都不会,至少使用标准接口,所以你需要在顶层构建一个层,或者只在一个线程中使用它们。线程安全的那些让你提供他们修改的状态,这样你就可以有效地运行多个不相互作用的随机数生成器,这可能不是你想要的。