【问题标题】:How does modulus and rand() work?模数和 rand() 是如何工作的?
【发布时间】:2013-10-23 22:11:18
【问题描述】:

所以,我对此很着迷。

rand() % 6 将始终产生 0-5 之间的结果。

但是当我需要在 6-12 之间时。

我应该有 rand() % 6 + 6

0+6 = 6.
1+6 = 7.
...
5+6 = 11. ???

如果我想要 6-12 的区间,我需要 + 7 吗?但是,0+7 = 7。什么时候会随机化 6 个?

我在这里缺少什么?哪一种是在 6 到 12 之间获得随机数的正确方法?为什么?好像我在这里遗漏了什么。

【问题讨论】:

  • 附近有两只手吗?数一数你的手指,6到12之间有多少个数字?您认为% 运算符需要什么?
  • 当然,Daniel,随心所欲地嘲笑我,但是我正在尝试另一个 SO 问题提供的“算法”,即 rand()%(max-min)+min;纠正我如果我错了,12-6 = 6。最小值是 6。所以 rand() % 6 + 6。这就是我感到困惑的原因。
  • @John:这是一种轻松的方式告诉你算错了。你想要七个不同的值,你放在%右边的参数是你想要得到多少个不同的值。您现在提到的算法是半开区间([min, max));有几个理由永远不要使用它(关于rand()%);请参阅 Shafik 的答案以了解正确的方法。告诉您使用% 生成某个范围内的随机数的人可能太无知而无法指导任何人。如果是教授,请随时将这个问题的链接发给他,我们会直截了当。

标签: c++ random


【解决方案1】:

如果C++11 是一个选项,那么您应该使用random headeruniform_int_distrubution。正如 James 在使用 rand% 的 cmets 中指出的那样,存在很多问题,包括有偏差的分布:

#include <iostream>
#include <random>

int main()
{
    std::random_device rd;

    std::mt19937 e2(rd());

    std::uniform_int_distribution<int> dist(6, 12);

    for (int n = 0; n < 10; ++n) {
            std::cout << dist(e2) << ", " ;
    }
    std::cout << std::endl ;
}

如果您必须使用rand,那么应该这样做:

rand() % 7 + 6

更新

使用rand 的更好方法如下:

6 + rand() / (RAND_MAX / (12 - 6 + 1) + 1)

我从 C 常见问题解答 中获得了这个,它在How can I get random integers in a certain range? 问题中得到了解释。

更新 2

Boost 也是一个选项:

#include <iostream>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int_distribution.hpp>

int main()
{
  boost::random::mt19937 gen;
  boost::random::uniform_int_distribution<> dist(6, 12);

  for (int n = 0; n < 10; ++n) {
    std::cout << dist(gen) << ", ";
  }
  std::cout << std::endl ;
}

【讨论】:

  • @ktodisco:又是the XY problem,最好只给出他需要的答案而不是他要求的答案。
  • @DanielKO 我认为更好的答案会解释具体问题的解决方案,然后给出更好解决方案的建议。看来这个答案已经过编辑,但没有解释前者......
【解决方案2】:

你需要 rand() % 7 + 6。

rand() %7 中的最小数:0。 rand() %7 中的最高数字:6。

0 + 6 = 6。 6 + 6 = 12。

【讨论】:

    【解决方案3】:

    模运算a % b 计算除法a / b 的余数。显然,除法的余数必须小于b,如果a 是随机正整数,则a%b 是0 .. (b-1) 范围内的随机整数


    在您提到的 cmets 中:

    rand()%(max-min)+min

    此算法生成半开范围 [min, max) 中的值。 (也就是说,max 在范围之外,只是表示边界。由于我们讨论的是整数范围,所以这个范围相当于封闭范围 [min, max-1]。)

    当您写“0 - 5”或“6 - 12”时,它们是封闭范围。要使用上述等式,您必须使用表示等效半开范围的值:[0, 6) 或 [6, 13)。

    rand() % (6-0) + 0
    
    rand() % (13-6) + 6
    

    请注意,rand() % (max-min) + min 只是您显然已经了解的规则的概括:rand() % n 产生范围为 0 - (n-1) 的值。等效的半开范围是 [0, n) 和 rand() % n == rand() % (n-0) + 0

    所以教训是:不要将半开范围与封闭范围混淆。


    第二个教训是,这显示了&lt;random&gt;rand() 更易于使用并手动计算您自己的分布的另一种方式。内置的uniform_int_distribution 允许您直接声明所需的包含范围。如果你想要范围 0 - 5,你说 uniform_int_distibution&lt;&gt;(0, 5),如果你想要范围 6 - 12,你说 uniform_int_distribution&lt;&gt;(6, 12)

    #include <random>
    #include <iostream>
    
    int main() {
      std::default_random_engine eng;
      std::uniform_int_distribution<> dist(6, 12);
    
      for (int i=0; i<10; ++i)
        std::cout << dist(eng) << " ";
    }
    

    rand()%7+6 更简洁,但这并不意味着它更易于使用。

    【讨论】:

      【解决方案4】:

      这里有几个概念。

      首先,有范围:[ 表示包含。 ) 表示独占。

      模数运算符 % n 产生[0,n) - 0,..n-1

      当您向该结果添加一个值时,您将相同的值添加到范围的两端:

      %n + 6 = [n,n+6)

      现在,如果 n 为 6,就像你的情况一样,你的输出将是 [0,6)

      %6 + 6 = [6,12)

      现在你想要的是[6,13)

      从中减去 6:

      [0,7) + 6 =&gt; n%7 + 6

      其次,还有使用 rand() 的问题。这取决于您是否真的关心您的数据是否是随机的。如果你真的很在意它是随机的,我强烈建议在今年的 Going Native 2013 上观看 Stefan T Lavalej 在the pitfalls of using rand() 上的演讲。他还谈到了你应该使用什么。

      如果 rand() 的随机性对你来说并不重要,那么一定要使用它。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-10-20
        • 2011-02-09
        • 2015-08-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-07-28
        • 1970-01-01
        相关资源
        最近更新 更多