为什么rand()每次运行都会产生相同的数字序列?

54
每次我用 rand() 运行程序时,它都给我相同的结果。
例子:
#include <iostream>
#include <cstdlib>

using namespace std;

int random (int low, int high) {
    if (low > high)
        return high;
    return low + (rand() % (high - low + 1));
}

int main (int argc, char* argv []) {
    for (int i = 0; i < 5; i++)
        cout << random (2, 5) << endl;
}

输出:

3
5
4
2
3
每次我运行程序时,它输出的数字都是相同的。有没有什么办法可以避免这种情况发生?

实际上,行为似乎在某个时刻发生了逆转。除非我使用相同的参数调用srand(),否则我无法获得相同的序列。如果不调用srand(),序列就不会重复。这是在gcc(Debian 12.2.0-14)12.2.0下的情况。 - undefined
7个回答

79
随机数生成器的种子没有设置。
如果您调用srand((unsigned int)time(NULL)),则会获得更多的随机结果:
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;

int main() {
    srand((unsigned int)time(NULL));
    cout << rand() << endl;
    return 0;
}

原因在于从rand()函数生成的随机数实际上并不是真正的随机数,而只是一种转换。维基百科对伪随机数生成器的含义给出了更好的解释:确定性随机位生成器。每次调用rand()时,它都会获取种子和/或上次生成的随机数(C标准没有指定使用的算法,尽管C++11具有指定一些流行算法的功能),对这些数字进行数学运算,并返回结果。因此,如果种子状态每次相同(如果您没有使用真正的随机数调用srand),则将始终获得相同的“随机”数字。
如果您想了解更多信息,可以阅读以下内容:

http://www.dreamincode.net/forums/topic/24225-random-number-generation-102/

http://www.dreamincode.net/forums/topic/29294-making-pseudo-random-number-generators-more-random/


如果我在一个.h文件中有这个函数怎么办?如果不是在main函数中,那么设置种子的合适位置在哪里? - David Montgomery
你可以将其设置在一个对象的构造函数中,该对象会在rand()第一次调用之前被构造(例如,在必要时使用静态/单例对象)。 - Jeremy Friesner

21
如果在调用srand()之前调用rand(),它将隐式地被视为已经调用了srand(1)。 标准C99 7.20.2.2cstdlib 基于该标准)中的相关部分如下所述:

如果在任何对srand的调用之前调用rand,则将生成与首次使用种子值1调用srand时相同的序列。

换句话说,每次都会得到相同的序列。您可以将您的main更改为:

int main (int argc, char* argv []) {
    srand (time (0));  // needs ctime header.
    for (int i = 0; i < 5; i++)
        cout << random (2, 5) << endl;
    wait ();
}

要解决这个问题,假设你不会在一秒内运行它超过一次。

如前所述,你需要使用 ctime 头文件。你还应该引入 cstdlib,因为那里有 randsrand。通常最好使用 cXXX 的头文件而不是 XXX.h 的头文件(例如使用 cmath 而不是 math.h)。

因此,在进行所有这些更改后(并且使用显式命名空间,我喜欢但其他人可能不喜欢),最终代码会是:

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

void wait () {
    int e;
    std::cin >> e;
}

int random (int low, int high) {
    if (low > high) return high;
    return low + (std::rand() % (high - low + 1));
}

int main (int argc, char* argv []) {
    std::srand (std::time (0));
    for (int i = 0; i < 5; i++)
        std::cout << random (2, 5) << '\n';
    wait ();
}

每次运行该程序,它都会给出一个不同的序列,至少在运行几次之后是这样的。显然,数据重复的硬性限制存在(只有45种可能性),而输出的“随机”性质也意味着它可能在此之前就会重复:-)


3

这是rand()函数的一个特性。

你拥有的不是随机数生成器,而更严格地说是一种"伪随机数生成器"。能够为相同的种子(使用srand(x)函数进行种子)产生相同的随机序列对于重现错误或在程序运行之间保留状态可能很重要。

个人而言,我使用这个功能来能够暂停/持久化基于蒙特卡罗的地形渲染进程。一个好的副作用是,你能够保证在不同的机器上进行不同的蒙特卡罗实验,从而能够生成保证不同的结果,然后在最终步骤中将其减少到更高质量的最终结果(当然,您可以稍后重复使用此更高质量的最终结果来产生更高质量的结果)。

需要注意的是,无论是C还是C++都没有定义从rand()生成的数字序列。因此,如果您需要跨平台保证序列,请使用C++11的新随机数生成器之一(例如Mersenne Twister),自己编写(有些生成器几乎可以轻松掌握,但由于它们大多依赖于特定的溢出行为,因此它们的实现可能并不容易),或使用第三方组件(例如boost::random)。


1
你需要初始化随机数生成器(请见函数'srand')。假设你不是在做密码学,那么用'time'的输出来初始化它可能已经足够了。

0

C++使用伪随机数生成器。这基本上意味着您的程序正在读取一个极长的预定数字列表。当您反复运行程序时,它每次都会给出相同的数字,因为它每次都是从该数字列表的开头开始读取。

您可以设置程序从列表中的第n个数字开始。您可以使用时间、温度或任何其他“随机”因素。(对于温度,您可以取温度的最后一位数字,或使用其后面的小数位。)


0

你实际上获得的是伪随机数。为了使它们更加“随机”,您可以使用某些“变化”的东西(最常见的是当前时间)来设置随机数生成器的种子。


1
注意:从数学上讲,这并不会使它们“更随机”。 - Sebastian Mach
“True, but in the context of the OP's question... (plus it's in quotes ;-)” - John3136

-1

使用randomize()函数,它会自动生成种子值。 或者,如果你想使用rand()函数,那么可以通过使用srand(seedvalue)函数来生成种子值;种子值可以是任何值,例如系统时间...这样每次都会得到不同的随机数。


5
randomize 不是标准的C++函数。你在谈论哪个平台? - Sebastian Mach

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