【问题标题】:Generating a uniform random integer in C++在 C++ 中生成一个统一的随机整数
【发布时间】:2013-04-15 17:41:52
【问题描述】:

问题是我需要生成一个 0 到 999 之间的随机整数(用于研究数学猜想)。所有值都需要具有相同的出现概率。

我已经尝试过rand(),但是RAND_MAX 是 32767(在我的编译器上)这意味着仅采用 rand() % 1000 导致第一个 1-767 出现的可能性要大得多(这是假设所有首先,rand() 中的可能性具有相同的概率)。

我使用的是 Windows,所以/dev/random 不是一个选项。

【问题讨论】:

  • 找不到dup,但你需要1000 * (((double)rand()) / RAND_MAX)
  • 你的意思不是真正随机的。这意味着别的东西。夸大其词地说“真正”随机就像滥用“字面意思”一样。
  • 值得注意的是,“真正随机”和“均匀分布”是不同的概念。由于您使用的是伪随机数生成,因此您不是在寻找真正随机的东西。
  • @user93353 不,那是 C,这是 C++。
  • 这个问题绝对不是C问题的重复。

标签: c++ random


【解决方案1】:

你可以在 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';
}

【讨论】:

  • 这看起来像我一直在寻找的东西。不幸的是,我无法访问 C++11。
  • @user1546083:所有这一切都以 C++03 兼容形式增强。
  • 这应该适用于 vs2012,包括 random_device 是一个非确定性 pRNG(我相信基于 Windows 的加密服务)。
  • 这个解决方案还可以,但我不喜欢它的风格。实际上,在某些情况下我需要生成许多具有不同参数极值的数字,例如来自 Uniform[0,10]、Uniform[0,12] 等的数字。有没有办法在调用方法时插入这些参数(而不是在构造对象时)??
【解决方案2】:

您的模数观察是正确的,这也是rand() 经不起数学审查的几个 原因之一。来自the notes here:

我们无法保证生成的随机序列的质量。过去,rand() 的一些实现在产生的序列的随机性、分布和周期方面存在严重缺陷(在一个众所周知的示例中,低位位在调用之间简单地在 1 和 0 之间交替)。不建议将 rand() 用于严重的随机数生成需求,例如密码学。

C++11 引入了several new random number generators,它遵循更严格的标准,可能适合您的目的..

如果您可以牺牲超过几个字节的开销(假设可以),我推荐std::mersenne_twister_engine

【讨论】:

  • 请确保您正确初始化了 mersenne twister。标准的基于时间的方法是不够的。
【解决方案3】:

我认为最简单的方法是扔掉区间[32000, 32767] 中的数字,只将% 1000 应用于剩余的数字。这应该会让你的分布更加均匀。

或者,您可以使用 boost 的随机/均匀分布组件(或来自 C++11,如果可用),因为它们将提供比 rand 更可靠的 PRNG。

【讨论】:

    【解决方案4】:

    问题在于 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() 值)。截断的均匀分布仍然是均匀的,因此您将在新域上拥有新的均匀分布变量(这将是更准确的条件分布)。统计示例:

    因此截断的均匀分布将具有另一个“矩”,即描述它的参数(均值、std_dev、方差等),但将再次保持均匀。

    示例代码:

    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();
    
    }
    

    【讨论】:

    • 为什么说“你真的应该使用 boost”,然后再举一个不使用 boost 的例子?
    • 耐心与它有什么关系?我评论了你的回答。如果您的答案不完整,为什么要发布它?
    • 当然,这个“int output = min + (rand() % (int)(max - min + 1))”是我已经在做的,因为 min = 0。
    • 您为什么认为表达式min+((double)rand()/(double)RAND_MAX)*(max-min) 会导致均匀分布?它仍在将RAND_MAX 输入映射到max-min 输出。因此,假设每个可能的输入都具有相同的可能性,除非RAND_MAX 可以被max-min 整除,否则某些输出将比其他输出更有可能,因此它是不均匀的。我很确定这就是 MooingDuck 投票的原因。
    • 没有。不对。也许这将证明这一点:ideone.com/5IjSNA -- 您的解决方案也有类似的问题,尽管它不那么明显。
    【解决方案5】:
    1. 使用rand() 获取随机数。

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

    3. 将此数字乘以 1000。

    【讨论】:

    • 我怀疑浮点舍入问题在乘法后不会偏向某些整数。
    • 例如,由于四舍五入,1000 出现的次数远远少于时间的 1/1000。
    • 这只是将 (0-RAND_MAX) -> (0-1000) 的分布从最低的 767 更改为不可预测的 727。这不会使它们变得均匀。
    【解决方案6】:

    计算机中没有“真正随机”这样的东西。我也不相信 1-767(或技术上,在这种情况下为 0-767)的机会明显高于任何其他数字。但是,如果您需要“更好”的随机数,那么 C++11 支持使用 Mersenne Twister,这是一种更高等级的随机数生成器。

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

    【讨论】:

    • 在 Windows 上,对于 1000 个值,选择较小数字的频率比较高数字高 1.539%。这可能对他的工作意义重大。
    • 我对 Windows 的 RAND_MAX 有误,选择较小的数字的频率增加了 3.125%。
    猜你喜欢
    • 2011-09-18
    • 1970-01-01
    • 2015-01-28
    • 1970-01-01
    • 2013-12-07
    • 2018-04-03
    • 1970-01-01
    • 2019-02-15
    相关资源
    最近更新 更多