C++生成随机数

vis*_*ner 22 c++

我的输出是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; 
    } 
}
Run Code Online (Sandbox Code Playgroud)

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

Rob*_*obᵩ 38

因为,在你的平台上,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)))
Run Code Online (Sandbox Code Playgroud)

  • #define catch_of_the_day这个 (7认同)
  • 请注意,如果使用此方法,某些值将比其他值更频繁地出现.这在数学上并不正确. (4认同)

bam*_*s53 16

<random>正确使用库比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) << " ";
}
Run Code Online (Sandbox Code Playgroud)


ryo*_*ung 11

random_integer = (rand() % 10) + 1 
Run Code Online (Sandbox Code Playgroud)

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

  • 使用'%`对你的概率分布是不好的,在这种情况下它很难并不重要. (7认同)
  • @hochl使用`%`实际上是首选方法,如果你有一个像样的随机数生成器.无论采用何种方法,您都必须抛弃一些值,或者在结果中接受一些偏差. (6认同)
  • @phresnel你在减少范围之前扔掉`rand()`的某些输出.如果你想要十个不同值中的一个,并且`rand()`产生一些不是十的倍数的值,你必须扔掉一些,或者比其他值更频繁地返回一些值. (4认同)
  • @dmckee如果您正在使用随机数进行认真的工作,并且随机生成的不同数字的数量不是您感兴趣的元素数量的精确倍数,则必须抛弃一些生成的随机数数字,或者你引入偏见.我认识的那些正在进行蒙特卡罗分析的人通常会寻找一个"双重"结果,其中包含与生成可以给出的不同的值; 在这种情况下,当然,你不会使用`%`. (2认同)

Jam*_*nze 9

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

首先,当然,原始代码中的问题是range * rand()优先于以下除法的事实 ,并且使用整数运算来完成.取决于RAND_MAX,这很容易导致溢出,实现定义结果; 在我知道的所有实现中,如果它确实导致溢出(因为 RAND_MAX > 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;
Run Code Online (Sandbox Code Playgroud)

(有几种方法可以确定要丢弃的值.这恰好是我使用的那个,但我记得Andy Koenig使用了一些完全不同的东西 - 但最终会导致相同的值被抛出.)

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

请注意,这些注释仅适用于需要固定数量的离散结果的情况.对于范围内产生一个随机浮点数的(其他)常见的情况[0,1),rand() / (RAND_MAX + 1.0)是因为你要得到的一样好.