【问题标题】:C++ generating random numbersC++ 生成随机数
【发布时间】:2012-03-18 01:56:47
【问题描述】:

我的输出是 20 个随机 1,而不是介于 10 和 1 之间,谁能解释为什么会这样?

#include <iostream> 
#include <ctime> 
#include <cstdlib>

using namespace std;

int main() 
{ 
    srand((unsigned)time(0)); 
    int random_integer; 
    int lowest=1, highest=10; 
    int range=(highest-lowest)+1; 
    for(int index=0; index<20; index++){ 
        random_integer = lowest+int(range*rand()/(RAND_MAX + 1.0)); 
        cout << random_integer << endl; 
    } 
}

输出: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

【问题讨论】:

  • 我只能生成一个 1,没有随机数生成作用于我的代码,我不知道其中的实际问题,请任何人清除我的代码...
  • 我可以建议像(rand() % range) + lowest 这样的东西吗?
  • @JoachimPileborg,你的建议非常好,它有帮助,但有一个小问题,我需要的答案在 1 到 10 之间,但它有时会产生 0 而没有数字 10... .
  • @Joachim 不幸的是,您的建议实际上非常糟糕,因为它使随机数可能更加偏颇。
  • 当然不是,任何严肃的应用程序都应该使用好的随机数源。但是对于像音轨改组这样简单的东西,没有人会注意到其中的区别。

标签: c++


【解决方案1】:

因为,在您的平台上,RAND_MAX == INT_MAX。

表达式range*rand() 的值永远不能大于INT_MAX。如果数学表达式大于INT_MAX,则整数溢出将其减少到介于INT_MIN 和INT_MAX 之间的数字。将其除以RAND_MAX 将始终产生零。

试试这个表达式:

random_integer = lowest+int(range*(rand()/(RAND_MAX + 1.0)))

【讨论】:

  • #define catch_of_the_day this
  • 请注意,如果您使用此方法,某些值会比其他值更频繁地出现。这在数学上不正确。
【解决方案2】:

正确使用&lt;random&gt; 库比rand 容易得多(假设您对C++ 足够熟悉,语法不会让您感到困惑)。

#include <random>
#include <iostream>

int main() {
  std::random_device r;
  std::seed_seq seed{r(), r(), r(), r(), r(), r(), r(), r()};
  std::mt19937 eng(seed);

  std::uniform_int_distribution<> dist(1, 10);

  for(int i = 0; i < 20; ++i)
    std::cout << dist(eng) << " ";
}

【讨论】:

  • 更简单?嗯,这很难卖;-)。使用&lt;random&gt; 的主要原因是它让您可以灵活地插入不同的随机分布,例如正态分布或泊松分布,以及插入更好或更快的不同数量的随机生成器。
  • 这是使用&lt;random&gt; 的替代方法:gist.github.com/1946214 我添加了一些cmets。它不需要 C++11。我希望它能显示正在发生的事情。
  • 好吧,比方说更难出错。也比正确使用rand() 更易读和更明显。当然,您可以将使用 rand() 的代码封装到一个函数中,使其与 &lt;random&gt; 一样具有可读性,但既然 &lt;random&gt; 已经存在,何必呢?
  • 我会说使用它更容易,因为这个问题存在的原因是因为转换 rand() 以产生您实际需要的输出存在细微的困难。
【解决方案3】:
random_integer = (rand() % 10) + 1 

这应该会给你一个介于 1 和 10 之间的伪随机数。

【讨论】:

  • 使用% 不利于您的概率分布,在这种情况下很难,这并不重要。
  • @hochl 如果你有一个不错的随机数生成器,使用% 实际上是首选方法。无论采用哪种方法,您都必须丢弃一些值,或者接受结果中的一些偏差。
  • @phresnel 在缩小范围之前,您丢弃了rand() 的某些输出。如果您想要十个不同的值之一,而rand() 产生的值不是十的倍数,则您必须丢弃一些值,或者比其他值更频繁地返回该范围内的一些值。
  • @JamesKanze 我不知道蒙特卡洛社区中有人提倡以这种方式使用模数(您描述的两步过程的 ryoungs 示例)。 Rob 的帖子中的方法阅读起来比较麻烦但正确,而且它是 idomatic 所以你停止阅读它。
  • @dmckee 如果您正在认真处理随机数,并且随机生成的不同数字的数量不是您感兴趣的元素数量的精确倍数,那么您有丢弃一些生成的随机数,或者引入偏差。我认识的进行蒙特卡洛分析的人通常在寻找double 结果,生成器可以提供尽可能多的不同值;在这种情况下,您当然不会使用%。
【解决方案4】:

一个有点晚的答案,但它应该提供一些额外的 信息如果生成的质量很重要。 (不是所有 应用程序需要这个——轻微的偏差通常不是问题。)

首先,当然,原始代码中的问题在于: range * rand() 优先于下面的除法,完成 使用整数算术。根据RAND_MAX,这可以很容易 导致溢出,实现定义的结果;所有 我知道的实现,如果它确实导致溢出(因为 RAND_MAX &gt; INT_MAX / range,实际结果几乎可以肯定 小于RAND_MAX + 1.0,除法将导致 值小于1.0。有几种方法可以避免这种情况: 最简单最可靠的就是rand() % range + lowest。

请注意,这假定rand() 具有合理的质量。许多 早期的实现不是,我至少见过一个 rand() % 6 + 1 模拟掷骰子的奇偶交替。这 这里唯一正确的解决方案是更好地实施 rand();它导致人们尝试替代解决方案,例如 (range * (rand() / (RAND_MAX + 1.0))) + lowest。这掩盖了 问题,但它不会将坏的生成器变成好的生成器。

第二个问题,如果一代的质量很重要,是 在生成随机整数时,您正在离散化:如果您是 模拟掷骰子,例如,你有六个可能 值,您希望以相等的概率出现。随机的 生成器将生成RAND_MAX + 1 不同的值,相等 可能性。如果RAND_MAX + 1 不是 6 的倍数,则没有 将值平均分配到所需的 6 的可能方式 价值观。想象一下RAND_MAX + 1 是 10 的简单情况。使用 % 上面的方法,值 1-4 的可能性是 值 5 和 6。如果使用更复杂的公式 1 + int(6 * (rand() / (RAND_MAX + 1.0)))(在 RAND_MAX + 1 == 10 的情况下, 事实证明,3 和 6 的可能性只有其他值的一半。 从数学上讲,根本没有办法分配 10 个不同的 将值放入 6 个插槽中,每个插槽中的元素数量相同。

当然,RAND_MAX 总是比 10 大很多,并且 引入的偏差将大大减少;如果范围是 明显低于RAND_MAX,可以接受。如果它是 不是,但是,通常的程序是这样的:

int limit = (RAND_MAX + 1LL) - (RAND_MAX + 1LL) % range;
            //  1LL will prevent overflow on most machines.
int result = rand();
while ( result >= limit ) {
    result = rand();
}
return result % range + lowest;

(有几种方法可以确定要丢弃的值。这个 碰巧是我用的那个,但我记得安迪·科尼格用过什么东西 完全不同——但导致相同的值 最后扔掉了。)

请注意,大多数情况下,您不会进入循环;最坏的情况是 当range 是(RAND_MAX + 1) / 2 + 1,在这种情况下,你仍然会 在循环中平均不到 1 次。

请注意,这些 cmets 仅适用于您需要固定数量的 离散结果。对于生成随机数的(其他)常见情况 [0,1)、rand() / (RAND_MAX + 1.0) 范围内的浮点数与您将获得的一样好。

【讨论】:

    【解决方案5】:

    Visual Studio 2008 对该程序完全没有问题,并且可以愉快地生成一组随机数。

    我要小心的是 /(RAND_MAX +1.0),因为这可能会与整数问题发生冲突并最终导致一个大的零。

    在除法之前转换为 double,然后再转换回 int

    【讨论】:

    • RAND_MAX+1.0 是双精度数,根据通常的算术转换,C++03,§5,¶9。
    【解决方案6】:

    我建议您将rand()/(RAND_MAX + 1.0) 替换为range*double(rand())/(RAND_MAX + 1.0))。由于我的解决方案似乎让人头疼......

    可能的参数组合:

    • range*rand() 是一个整数,会溢出。
    • double(range*rand()) 在转换为 double 之前溢出。
    • range*double(rand()) 没有溢出并产生预期结果。

    我原来的帖子有两个大括号,但它们没有改变任何东西(结果是一样的)。

    【讨论】:

    • 这不会改变任何事情,因为第二个操作数已经是double。
    • 有趣的是,OPs 版本给出了所有 1,而这个 diff 给出了随机数。你试过了吗?
    • 关键区别在于您使用了额外的括号,而不是显式转换为 double。我应该这么说的。
    • 抱歉,我仍然没有关注你——对我来说,他的代码的一个问题是range*rand() 已经溢出,将rand() 转换为double 将避免这种情况,因为double有 57 位精度。
    • 道歉。我完全忘记了range 乘数,我只是看了你的一段代码,看到了一个多余的演员表。你说的对。感谢您的坚持。
    【解决方案7】:
    (rand() % highest) + lowest + 1
    

    【讨论】:

      【解决方案8】:

      可能“10 * rand()”小于“RAND_MAX + 1.0”,所以你计算的值为0。

      【讨论】:

        【解决方案9】:

        您正在生成一个随机数(即(range*rand()/(RAND_MAX + 1.0))),其值在-1 和1 之间(]-1,1[),然后将其转换为整数。此类数字的整数值始终为 0,因此您最终会得到 lower + 0

        编辑:添加公式以使我的答案更清晰

        【讨论】:

        • rand() 返回一个介于 0 和 RAND_MAX 之间的数字。
        • @Gandaro:我说的不是 rand(),而是语句 (range*rand()/(RAND_MAX + 1.0))....
        • 好的,那么一切都很好。 ;)
        【解决方案10】:

        如果使用条件来检查最后一个数字是否与当前数字相同呢?如果满足条件,则生成另一个随机数。此解决方案有效,但需要更多时间。

        【讨论】:

          【解决方案11】:

          这是最简单的逻辑之一,从博客中获得。在这个逻辑中,您可以在 for 循环中使用给定的模数 (%) 运算符限制随机数,它只是从该博客复制和粘贴,但无论如何检查一下:

          // random numbers generation in C++ using builtin functions
          #include <iostream>
          
          using namespace std;
          
          #include <iomanip>
          
          using std::setw;
          
          #include <cstdlib>   // contains function prototype for rand
          
          int main()
          {
          // loop 20 times
          for ( int counter = 1; counter <= 20; counter++ ) {
          
              // pick random number from 1 to 6 and output it
              cout << setw( 10 ) << ( 1 + rand() % 6 );
          
              // if counter divisible by 5, begin new line of output
              if ( counter % 5 == 0 )
                  cout << endl;
          
          }
          
          return 0;  // indicates successful termination
          
          } // end main
          

          - 查看更多信息:http://www.programmingtunes.com/generation-of-random-numbers-c/#sthash.BTZoT5ot.dpuf

          【讨论】:

            猜你喜欢
            • 2015-07-19
            • 1970-01-01
            • 2011-01-24
            • 2019-03-30
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多