srand()—为什么只调用一次?


Answers:


109

这取决于您要实现的目标。

随机化是作为具有起始值的函数(即seed)执行的

因此,对于相同的种子,您将始终获得相同的值序列。

如果您每次需要一个随机值时都尝试设置种子,并且种子是相同的数字,则您将始终获得相同的“随机”值。

种子通常从目前的时间,这是秒,在拍摄time(NULL),所以如果你总是设置种子取随机数之前,你会得到相同的数量,只要你调用函数srand /兰特组合多次在同一秒

为避免此问题,每个应用程序只能设置一次srand,因为两个应用程序实例将在同一秒内初始化是令人怀疑的,因此每个实例将具有不同的随机数序列。

但是,您极有可能在一秒钟内多次运行您的应用程序(特别是短程序或命令行工具之类的应用程序),那么您将不得不采用其他方法来选择种子(除非您可以在不同的应用程序实例中使用相同的顺序)。但是就像我说的那样,这取决于您的应用程序使用情况。

另外,您可能希望尝试将精度提高到微秒(以最小化相同种子的机会),要求(sys/time.h):

struct timeval t1;
gettimeofday(&t1, NULL);
srand(t1.tv_usec * t1.tv_sec);

3
旁注:gettimeofday在POSIX 2008中已作废。相反,它引入clock_gettime了可能需要与链接的方法-lrt。但是,它可能在许多平台上均不可用。在Linux中,这没关系。在Mac上,我认为它尚不可用。在Windows中,它可能永远不可用。
Shahbaz 2013年

1
t1.tv_usec是一个long int,srand将一个无符号int作为输入。(我只是遇到了一个问题,这个问题会
有所作为

24

随机数实际上是伪随机数。首先设置一个种子,每次调用rand都会从中获得一个随机数,并修改内部状态,并且在下一次rand调用中使用此新状态来获取另一个数。由于使用某个公式来生成这些“随机数”,因此在每次调用后设置一定的种子值rand将从该调用返回相同的数字。例如srand (1234); rand ();将返回相同的值。使用种子值初始化一次初始状态将生成足够的随机数,因为您无需使用设置内部状态srand,从而使这些数字更有可能是随机的。

通常,time (NULL)在初始化种子值时,我们使用返回的秒值。说srand (time (NULL));是一个循环。然后,循环可以在一秒内迭代一次以上,因此循环在循环中第二次rand调用中在循环内迭代的次数将返回相同的“随机数”,这是不希望的。在程序启动时对其进行一次初始化将设置一次种子,并且每次rand调用都将生成一个新数字并修改内部状态,因此下一次调用rand将返回一个足够随机的数字。

例如,以下代码来自http://linux.die.net/man/3/rand

static unsigned long next = 1;
/* RAND_MAX assumed to be 32767 */
int myrand(void) {
    next = next * 1103515245 + 12345;
    return((unsigned)(next/65536) % 32768);
}
void mysrand(unsigned seed) {
    next = seed;
}

内部状态next声明为全局。每次myrand调用都会修改并更新内部状态,并返回一个随机数。的每次调用myrand将具有不同的next值,因此该方法将在每次调用时返回不同的数字。

看一下mysrand实现;它只是设置您传递给的种子值next。因此,如果next每次调用前rand都将值设置为相同,则将返回相同的随机值,这是因为对其应用的公式相同,这是不希望的,因为该函数是随机的。

但是根据您的需要,您可以将种子设置为某个值,以在每次运行时生成相同的“随机序列”,例如针对某个基准或其他基准。


您不是要为mysrand()的参数表示(无符号长种子)吗?
Jiminion

@Jiminion这是的代码段man srand。范围是0到32767(假设RAND_MAX),比long范围小很多。状态变量next是由long作为内部乘法和加法将超过的范围内unsigned int。之后,在上述指定范围内缩放或修改结果。虽然你可以做种子long
phoxis

1
请注意,C标准也包括所示的代码片段。
乔纳森·莱夫勒

11

简短的回答:呼叫srand()不是就像“掷骰子”随机数发生器。这也不像洗牌一样。如果有的话,它更像是切开一副纸牌。

这样想吧。 rand()从一大副纸牌中进行交易,每次您调用它时,所要做的就是从纸牌顶部拾取下一张纸牌,给您值,然后将该卡返回到纸牌底部。(是的,这意味着“随机”序列将一段时间后重复这是一个。非常大的甲板,虽然:通常4,294,967,296卡)

此外,每一个程序运行时,卡一个全新的包从游戏商店买,卡每一个全新的包总是具有相同的序列。因此,除非您执行一些特殊的操作,否则每次运行程序时,它都会从返回完全相同的“随机”数字rand()

现在,您可能会说:“好吧,那我该如何洗牌呢?” 而答案-至少就randsrand关注-是不存在洗牌甲板的方式。

那怎么srand办?根据我在这里构建的类比,调用srand(n)基本上就像是说:“n从顶部剪切卡片组”。但是,等等,还有一件事:它实际上是从另一个全新的牌组开始,并n从顶部切下来的牌

所以,如果你打电话srand(n)rand()srand(n)rand(),...,用相同的n每一次,你不会只是得到一个不是非常随机序列,你就会得到来自相同数量的背部rand()每次。(可能不是您递给的相同号码srand,而是rand一遍又一遍地递回的相同号码。)

所以,你能做的最好的就是削减甲板上一次,也就是通话srand()一次,在程序的开始,用n在大甲板每次那是相当随机的,所以,你会开始在不同的随机位置的程序运行。使用rand(),这确实是您可以做的最好的事情。

[PS是的,我知道,在现实生活中,当您购买一副崭新的纸牌时,通常是有顺序的,而不是随机的。为了进行类比,我想像一下您从游戏商店购买的每个牌组都是看似随机的顺序,但与您从同一商店购买的其他所有牌组的看似随机的顺序完全一样。有点像他们在桥牌比赛中使用的相同洗牌的扑克牌。]


很棒的解释史蒂夫。
DrunkenMaster

8

原因是srand()设置了随机生成器的初始状态,并且如果您自己之间不触摸状态,则生成器生成的所有值仅“足够随机”。

例如,您可以这样做:

int getRandomValue()
{
    srand(time(0));
    return rand();
}

然后,如果您反复调用该函数,以便time()在相邻调用中返回相同的值,则只会得到生成的相同值-这是设计使然。


3

如图所示,一个更简单的解决方案用于srand()为在同一秒运行的应用程序实例生成不同的种子。

srand(time(NULL)-getpid());

该方法使您的种子非常接近随机,因为无法猜测线程何时启动,并且pid也将有所不同。


2

srand种子伪随机数生成器。如果调用不止一次,则将重新播种RNG。如果您使用相同的参数调用它,它将重新启动相同的序列。

为了证明这一点,如果您做一些简单的事情,例如:

#include <cstdlib>
#include <cstdio>
int main() {
for(int i = 0; i != 100; ++i) {
        srand(0);
        printf("%d\n", rand());
    }
}

您将看到相同的数字被打印100次。


2
问题是关于C,而不是C ++。
Spikatrix

0

1 \似乎每次rand()运行时,都会为下一个rand()设置新的种子。

2 \如果srand()运行多次,则问题是如果两个运行在一秒钟内发生(时间(NULL)不变),则下一个rand()将与前一个rand()相同srand()。


要点是,使用srand()相同的种子多次初始化会导致返回相同的值rand()
Thrushbeard国王
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.