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

C++ 在POSIX上生成随机双精度的最佳方法是什么?,c++,multithreading,random,posix,C++,Multithreading,Random,Posix,我希望得到[0.0,1.0]范围内的均匀分布 如果可能,请让实现使用/dev/uradom中的随机字节。 如果您的解决方案是线程安全的,那也很好。如果您不确定,请指出 看,我读了其他答案后想了想 #include <stdlib.h> printf("%f\n", drand48()); c是您的随机值从文件读取是线程安全的AFAIK,因此使用fopen()从/dev/uradom读取将产生“真正随机”的字节 尽管可能存在潜在的陷阱,但我认为任何一组以整数形式访问的字节,除以该大小

我希望得到[0.0,1.0]范围内的均匀分布

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

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

看,我读了其他答案后想了想

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

c是您的随机值

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

尽管可能存在潜在的陷阱,但我认为任何一组以整数形式访问的字节,除以该大小的最大整数,将产生一个介于0和1之间的浮点值,其分布大致相同

例如:

#包括
#包括
#包括
...
文件*f=fopen(“/dev/uradom”,“r”);
uint32_t i;
fread(&i,sizeof(i),1,f);//检查实际代码中的返回值!!
fclose(f);
双倍随机值=i/(双倍)(UINT32_MAX);

诀窍是你需要一个54位的随机化器来满足你的要求。几行代码加上一个并集,把这54位粘在尾数上,你就有了你的数字。诀窍不是双浮,而是你想要的随机化器。

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

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

复杂的:但是,在[0,1]范围内有许多以上无法生成的双值。具体而言,在[0,0.5]范围内的值(具有最低有效位的值)的一半无法出现。在[0,0.25]范围内的值的四分之三(具有至少两个位之一的值)不可能发生,等等,一路只有一个小于2^-51的正值是可能的,尽管一个双精度可以表示这种值的无数次。因此,不能说它在指定的范围内完全一致

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

我认为以下方法有效。我没有特别研究或测试过这个算法(你可能可以从没有代码的方式看出),而且我个人不会在没有找到正确的参考文献表明它有效的情况下使用它。但是下面是:

  • 指数从52开始,选择52位随机无符号整数(假设尾数为52位)
  • 如果整数的最高有效位为0,则将指数增加1,将整数左移1,并用新的随机位填充最低有效位
  • 重复这个过程,直到你在最重要的地方得到1,或者指数对于你的双精度(1023,或者可能是1022)来说太大
  • 如果你找到一个1,将你的值除以2^指数。如果你得到了所有的零,返回0(我知道,这实际上不是一个特例,但它强调了0返回的可能性有多大)[编辑:实际上这可能是一种特殊情况-这取决于您是否要生成去噪。如果不是,那么一旦您有足够多的0,您将丢弃剩下的任何内容并返回0。但实际上,这不太可能是可以忽略的,除非随机源不是随机的)

请注意,我不知道这样的随机双精度码是否有实际用途。您对随机数的定义应该在一定程度上取决于它的用途。但是,如果您能从它的所有52个有效位都是随机的中获益,这实际上可能会有所帮助。

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


无符号短整数r1、r2、r3;
//让r1、r2和r3保持随机值
双结果=ldexp(r1,-48)+ldexp(r2,-32)+ldexp(r3,-16);


这是基于NetBSD的drand48实现的。

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

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

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


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

这其中有一个错误-如果a或b为负数,那么比较不一定选择大小较小的值作为分子。此外,我太模糊了,不容易看到它产生均匀分布的输出。此外,在2^64中有一次你中了双大奖,并被未定义的行为除0。考虑到这一点,这如果其中一个值为0,则得到0,这几乎是2^31中的1,这是
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);
#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);
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);