srand() – 为什么只调用一次?
这个问题是关于这个问题的评论推荐的方法来初始化srand? 第一个评论说srand()
应该只在应用程序中被调用一次。 为什么这样?
这取决于你想要达到的目标。
随机化是作为具有起始值的函数执行的,即种子 。
所以,对于同样的种子,你总会得到相同的值序列。
如果每次需要随机值时尝试设置种子,并且种子数量相同,则始终会获得相同的“随机”值。
种子通常是从当前时间获得的,这是time(NULL)
,所以如果您在采用随机数之前始终设置种子,只要您调用srand / rand组合,就会得到相同的数字在同一时间多次。
为了避免这个问题,每个应用程序只设置一次srand,因为两个应用程序实例在同一秒内运行是值得怀疑的,所以每个实例都有不同的随机数序列。
但是,有一点可能性,你会在一秒钟内多次运行你的应用程序(特别是如果它是一个短的,或者一个命令行工具或类似的东西),那么你将不得不采取其他方式select一个种子(除非你在不同的应用程序实例中相同的序列是可以的)。 但正如我所说,这取决于您的应用程序的使用情况。
此外,您可能想要尝试将精度提高到微秒(最小化相同种子的机会),需要( sys/time.h
):
struct timeval t1; gettimeofday(&t1, NULL); srand(t1.tv_usec * t1.tv_sec);
随机数实际上是伪随机的。 首先设置种子, rand
每次调用都从中获得一个随机数,然后修改内部状态,并在下一个rand
调用中使用这个新的状态来获得另一个数。 由于使用了一个特定的公式来生成这些“随机数”,因此在每次调用rand
之后设置一个特定的种子值将会从调用返回相同的数字。 例如srand (1234); 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
的种子值。 因此,如果在调用rand
之前每次设置next
值相同,它将返回相同的随机值,因为应用了相同的公式,这是不可取的,因为该函数是随机的。
但根据您的需要,您可以将种子设置为某个特定的值,以生成每次运行相同的“随机序列”,例如对于某个基准testing或其他testing。
原因在于srand()
设置了随机生成器的初始状态,如果你自己不在触摸状态,所有生成器生成的值都是“随机的”。
比如你可以这样做:
int getRandomValue() { srand(time(0)); return rand(); }
然后如果你反复调用该函数,以便time()
在相邻的调用中返回相同的值,那么只需获得相同的值即可。
简单的回答:调用srand()
不像随机数发生器的“滚动骰子”。 也不像洗牌一样。 如果有的话,这更像是一副扑克牌。
像这样想。 rand()
从一副大卡牌中进行交易,每当你打电话给他时,只要从牌组顶端拿下下一张牌,给你价值,然后把这张牌返回到牌组的底部。 (是的,这意味着“随机”序列会在一段时间后重复出现,不过这是一个非常大的套牌,通常是4,294,967,296张牌。)
而且,每次程序运行时,都会从游戏商店购买一套全新的卡片,每一张全新的卡片都会有相同的顺序。 所以除非你做了一些特别的事情,否则每次你的程序运行时,都会从rand()
得到完全相同的“随机”数字。
现在,你可能会说,“好吧,那我该怎么洗牌呢?” 答案是(至less就rand
和srand
而言),没有一种方法来洗牌。
那么srand
做什么的? 根据我在这里build立的比喻,打电话给srand(n)
基本上就像是说:“从顶部切掉卡片”。 但是还有一件事,那就是再换一个全新的甲板,从上面砍下n
张牌 。
所以如果你每次都调用srand(n)
, rand()
, srand(n)
, rand()
,…,你不会得到一个非常随机的序列,实际上每次都从rand()
得到相同的数字。 (不一定是你交给srand
那个数字,而是从rand
那里一遍又一遍的相同数字。)
所以,最好的办法就是把卡组切割一次 ,也就是在程序开始时调用srand()
一次,这个n
是相当随机的,这样你就可以从一个不同的随机位置开始每次你的程序运行时,
[PS是的,我知道,在现实生活中,当你购买一副全新的卡片时,通常是按顺序排列的,而不是随机的。 在这里的比喻工作,我想象你从游戏商店购买的每一个套牌看起来是随机的顺序,但是看起来随机的顺序和你从同一个商店购买的所有其他套牌完全一样。 有点像他们在桥牌比赛中使用的相同的洗牌甲板。]
srand种子伪随机数发生器。 如果你不止一次地打电话给你,你会重新填入RNG。 如果你用相同的参数调用它,它将重新启动相同的序列。
为了certificate这一点,如果你做一些简单的事情:
#include <cstdlib> #include <cstdio> int main() { for(int i = 0; i != 100; ++i) { srand(0); printf("%d\n", rand()); } }
你会看到相同的号码打印100次。
使用srand生成应用程序实例的不同种子的更简单的解决scheme在同一秒运行。
函数srand(时间(NULL)-getpid());
这种方法使你的种子非常接近随机,因为没有办法猜测你的线程什么时候开始,而且pid也会不同。