在C ++中生成均匀分布的随机整数

7
问题是,我需要生成0到999之间的随机整数(用于研究数学猜想),所有值都需要具有相同的出现概率。
我尝试使用rand()函数,但由于RAND_MAX在我的编译器上为32767,这意味着仅执行rand() % 1000会导致前1-767个数出现的概率明显更高(假设所有可能性都具有相同的概率)。
由于我正在使用Windows操作系统,因此/dev/random不是一个选项。

找不到重复项,但您需要 1000 * (((double)rand()) / RAND_MAX) - Sergey Kalinichenko
1
你并不是真正意义上的随机。那意味着另外一件事情。说“真正意义上”的随机就像滥用“确实”一样,是夸张其词。 - djechlin
2
值得注意的是,“真正随机”和“均匀分布”是不同的概念。由于您正在使用伪随机数生成,因此您并不需要寻找真正随机的东西。 - krsteeve
1
@user93353 不是,那是C语言,这是C++。 - djechlin
5
这个问题绝对不是关于C语言的重复问题。 - bames53
显示剩余5条评论
6个回答

24

您可以使用 C++11 中的 uniform_int_distribution 来实现类似以下方式的操作:

#include <iostream>
#include <random>

int main()
{
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> dis(0, 999);

    for (int n=0; n<1000; ++n)
        std::cout << dis(gen) << ' ';
    std::cout << '\n';
}

1
那看起来就是我在寻找的东西。不幸的是,我没有访问C++11的权限。 - user1546083
1
@user1546083:所有这些都是以C++03兼容形式在boost库中实现的。 - Mooing Duck
这应该可以在vs2012中工作,包括random_device是一个非确定性pRNG(我相信基于Windows的加密服务)。 - bames53
这个解决方案还可以,但我不喜欢它的风格。实际上,在某些情况下,我需要使用不同参数极值生成许多数字,例如从Uniform[0,10]、Uniform[0,12]等中生成数字。有没有一种方法可以在调用方法时插入这些参数(而不是在对象构造时)? - altroware

10

您的模数观察是正确的,这也是rand()无法经受数学检验的几个原因之一。根据此处的说明

不能保证产生的随机序列的质量。过去,一些rand()的实现在生成的序列的随机性、分布和周期方面存在严重缺陷(在一个众所周知的例子中,低位比特只是在调用之间交替地为1和0)。不建议使用rand()进行严格的随机数生成需求,例如加密。

C++11引入了几个新的随机数生成器,这些生成器遵循更严格的标准,很可能适合您的目的。

如果您可以牺牲超过几字节的开销(可以安全地假设您可以),我建议使用std::mersenne_twister_engine


请确保正确初始化Mersenne Twister。标准的基于时间的方法是不够的。 - Richard

3

我认为最简单的方法是将区间[32000,32767]中的数字丢弃,只对其余数字应用% 1000。这应该能得到更加均匀的分布。

另外,您也可以使用boost的随机/均匀分布组件(或者如果有的话,使用C++11),因为它们会提供比rand更可靠的伪随机数生成器。


1
问题在于rand()函数会给你一个在区间[0,RAND_MAX]上均匀分布的变量,其中RAND_MAX最可能是32767。你不能通过简单的乘法将此域映射到较大的域中。
u=(double)rand();
d=(double)RAND_MAX;
double div= u/d;
double res=div*interval_range;

因为只有当RAND_MAX是interval_range的偶数倍时,这种方法才正确。然而,在您的较大域中,您将无法拥有所有值。但是,如果您的新期望域比RAND_MAX小,就像在您的情况下一样,您可以将由rand()生成的均匀分布截断到所需的域(这实际上意味着拒绝大于所需域的rand()值)。截断的均匀分布仍然是均匀的,因此您将在新域上拥有新的均匀分布变量(这将更精确地成为条件分布)。统计学示例:

enter image description here

所以截断均匀分布会有另外的“矩”,描述它的参数(平均值、标准差、方差等)但仍然是均匀的。

示例代码:

int main{ 
    int o=RAND_MAX;
    std::map<int,int> m1;
    int min=0,max=999;

    for (int i=0; i<1000*9994240; ++i){//9994240=305*32768  32768=RAND_MAX+1
        int r=rand();
        if(r<=max){
            m1[r]++;
        }
    }
    for (auto & i : m1)
        std::cout << i.first << " : " << i.second << '\n';
}

结果: 0 : 42637 1 : 42716 2 : 42590 3 : 42993 4 : 42936 5 : 42965 6 : 42941 7 : 42705 8 : 42944 9 : 42707 10 : 42860 11 : 43012 12 : 42793 //... 995 : 42861 996 : 42911 997 : 42865 998 : 42877 999 : 43159


你可以通过以下方式在任何域名上实现所需结果:

#include <iostream>
#include <random>

int main()
{
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> dis(0, 1000);

    for (int n=0; n<1000; ++n)
        std::cout << dis(gen) << ' ';
    std::cout << '\n';
}

然而在这种情况下,你应该真正使用 Boost:
#include <iostream>
#include "boost/random.hpp"
#include "boost/generator_iterator.hpp"
using namespace std;

int main() {
      typedef boost::mt19937 RNGType;
      RNGType rng;
      boost::uniform_int<> zero_to_n( 0, 999 );
      boost::variate_generator< RNGType, boost::uniform_int<> >
                    dice(rng, zero_to_n);
          int n  = dice();

}

3
为什么你会说“你真的应该使用boost”,然后又给出一个没有使用boost的例子? - Benjamin Lindley
1
耐心与此有什么关系?我已经在你的答案下评论了。如果你的回答不完整,为什么要发布它呢? - Benjamin Lindley
1
当然,"int output = min + (rand() % (int)(max - min + 1))" 就是我已经在做的事情了,因为 min = 0。 - user1546083
2
你为什么认为表达式 min+((double)rand()/(double)RAND_MAX)*(max-min) 会产生均匀分布?它仍然将 RAND_MAX 的输入映射到 max-min 的输出。因此,假设每个可能的输入都是等可能的,除非 RAND_MAX 能够被 max-min 均匀地整除,否则某些输出将比其他输出更有可能,因此它不是均匀的。我相信这就是MooingDuck投反对票的原因。 - Benjamin Lindley
1
不是。不正确。也许这个示例可以证明这一点:http://ideone.com/5IjSNA -- 尽管你的解决方案问题不太明显,但它也存在类似的问题。 - Benjamin Lindley
显示剩余26条评论

0
  1. 使用 rand() 函数获取一个随机数。

  2. 将其除以 RAND_MAX,你会得到一个介于 0 和 1 之间的浮点数。

  3. 将这个数乘以 1000。


2
我对浮点数舍入问题不会在乘法后对某些整数产生偏差持怀疑态度。 - djechlin
1
例如,由于向下取整,1000出现的次数远少于1/1000。 - djechlin
3
这只是改变了(0-RAND_MAX) -> (0-1000)范围内的数字分布,从原来的最低767变成不可预测的727,并不意味着它们变得均匀。 - Mooing Duck

0

计算机中不存在“真正随机”的概念。我也不相信1-767(或在那种情况下,技术上是0-767)比任何其他数字有显着更高的机会。然而,如果您需要“更好”的随机数,那么C++11支持使用Mersenne Twister,这是一种更高级别的随机数生成器。

这里有更多信息: http://www.cplusplus.com/reference/random/mt19937/


1
在Windows上,对于1000个值,较低的数字比较高的数字多选1.539%。这可能对他正在做的事情有重要意义。 - Mooing Duck
2
我之前对于Windows的RAND_MAX有误,较小的数字被选中的概率要高出3.125%。 - Mooing Duck

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接