4

我想在 [0.0, 1.0) 范围内得到均匀分布

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

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

查看我在阅读其他答案后想到的一些解决方案。

4

6 回答 6

4

这似乎是一个很好的方法:

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

于 2008-09-29T17:40:58.843 回答
3

简单:假设 IEEE,双精度为 52 位。所以生成一个 52 位(或更大)的无符号随机整数(例如通过从 dev/urandom 读取字节),将其转换为双精度数并将其除以 2^(它的位数)。

这给出了一个数值均匀的分布(因为一个值在给定范围内的概率与范围成正比)直到第 52 个二进制数字。

复杂:但是,在 [0,1) 范围内有很多上面无法生成的双精度值。具体来说,[0,0.5) 范围内的一半值(设置了最低有效位的值)不会出现。[0,0.25) 范围内的四分之三的值(设置了至少 2 位之一的值)不能发生,等等,一直到只有一个小于 2^-51 的正值是可能的,尽管 double 能够代表这种值的 squillions。所以不能说在指定范围内真正统一到全精度。

当然,我们不想以相等的概率选择其中一个双打,因为这样得到的数字平均来说太小了。我们仍然需要结果在给定范围内的概率与范围成正比,但在适用的范围上具有更高的精度。

认为以下工作。我没有特别研究或测试过这个算法(正如你可能通过没有代码的方式知道的那样),而且我个人不会在没有找到表明它有效的适当参考资料的情况下使用它。但这里有:

  • 从 52 开始指数并选择一个 52 位随机无符号整数(假设尾数为 52 位)。
  • 如果整数的最高有效位为 0,则将指数加 1,将整数左移 1,并用新的随机位填充最低有效位。
  • 重复直到你在最重要的地方打出 1,否则指数变得太大而不能让你的双倍(1023。或者可能是 1022)。
  • 如果您找到 1,请将您的值除以 2^exponent。如果你全为零,则返回 0 (我知道,这实际上不是一个特殊情况,但它强调了 0 返回是多么不可能[编辑:实际上它可能是一个特殊情况 - 这取决于你是否想要生成denorms. 如果没有,那么一旦你连续有足够的 0,你就丢弃剩下的任何东西并返回 0。但实际上这不太可能可以忽略不计,除非随机源不是随机的)。

请注意,我不知道这种随机双精度是否真的有任何实际用途。您对随机的定义应在一定程度上取决于它的用途。但是,如果您可以从其所有 52 个有效位的随机性中受益,那么这实际上可能会有所帮助。

于 2008-09-29T01:13:40.557 回答
1

从文件读取是线程安全的 AFAIK,因此使用 fopen() 从 /dev/urandom 读取将产生“真正随机”的字节。

尽管可能存在潜在的陷阱,但我认为作为整数访问的任何此类字节集,除以该大小的最大整数,将产生一个介于 0 和 1 之间的浮点值,近似于该分布。

例如:

#include <limits.h>
#include <stdint.h>
#include <stdio.h>
...
FILE* f = fopen("/dev/urandom", "r");
uint32_t i;
fread(&i, sizeof(i), 1, f);  // check return value in real world code!!
fclose(f);
double theRandomValue = i / (double) (UINT32_MAX);
于 2008-09-28T18:21:21.640 回答
0
#include <stdlib.h>
printf("%f\n", drand48());

/开发/随机:

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

于 2008-09-28T18:19:17.980 回答
0

诀窍是您需要一个满足您要求的 54 位随机发生器。几行带有联合的代码将这 54 位粘贴在尾数中,然后你就有了你的号码。诀窍不是双浮动,诀窍是您想要的随机化器。

于 2008-09-28T18:25:45.327 回答
0

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

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

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

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

于 2008-09-29T18:02:43.223 回答