srand() —为什么只调用一次?
这个问题是关于这个问题的评论 初始化 srand 的推荐方法? 第一条评论说 srand()< /code> 只能在应用程序中调用一次。为什么会这样呢?
This question is about a comment in this question
Recommended way to initialize srand? The first comment says that srand()
should be called only ONCE in an application. Why is it so?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
这取决于您想要实现的目标。
随机化作为具有起始值(即种子)的函数来执行。
因此,对于相同的种子,您将始终获得相同的值序列。
如果您每次需要随机值时都尝试设置种子,并且种子是相同的数字,那么您将始终获得相同的“随机”值。
种子通常取自当前时间,即秒,如
time(NULL)
所示,因此如果您始终在获取随机数之前设置种子,那么只要您在同一秒内多次调用 srand/rand 组合。为了避免这个问题,每个应用程序只设置一次 srand ,因为两个应用程序实例是否会在同一秒内初始化是值得怀疑的,因此每个实例将具有不同的随机数序列。
但是,您有可能在一秒钟内多次运行您的应用程序(特别是如果它很短,或者是命令行工具或类似的东西),那么您将不得不诉诸其他方式来选择种子(除非您可以接受不同应用程序实例中的相同序列)。但正如我所说,这取决于您的应用程序使用环境。
另外,您可能想尝试将精度提高到微秒(最大限度地减少相同种子的机会),需要(
sys/time.h
):That depends on what you are trying to achieve.
Randomization is performed as a function that has a starting value, namely the seed.
So, for the same seed, you will always get the same sequence of values.
If you try to set the seed every time you need a random value, and the seed is the same number, you will always get the same "random" value.
Seed is usually taken from the current time, which are the seconds, as in
time(NULL)
, so if you always set the seed before taking the random number, you will get the same number as long as you call the srand/rand combo multiple times in the same second.To avoid this problem, srand is set only once per application, because it is doubtful that two of the application instances will be initialized in the same second, so each instance will then have a different sequence of random numbers.
However, there is a slight possibility that you will run your app (especially if it's a short one, or a command line tool or something like that) many times in a second, then you will have to resort to some other way of choosing a seed (unless the same sequence in different application instances is ok by you). But like I said, that depends on your application context of usage.
Also, you may want to try to increase the precision to microseconds (minimizing the chance of the same seed), requires (
sys/time.h
):随机数实际上是伪随机的。首先设置一个种子,每次调用
rand
都会从中获取一个随机数,并修改内部状态,这个新状态将在下一次rand
调用中使用以获得另一个随机数数字。由于使用特定公式来生成这些“随机数”,因此每次调用rand
后设置特定的种子值都会从调用中返回相同的数字。例如 srand (1234); rand(); 将返回相同的值。使用种子值初始化初始状态将生成足够的随机数,因为您没有使用 srand 设置内部状态,从而使数字更有可能是随机的。通常我们在初始化种子值时使用
time (NULL)
返回的秒值。假设 srand (time (NULL)); 处于循环中。那么循环在一秒钟内可以迭代多次,因此循环中的第二个rand
调用中循环在循环内部迭代的次数将返回相同的“随机数”,这是不希望的。在程序启动时初始化一次,就会设置一次种子,每次调用rand
都会生成一个新的数字,并修改内部状态,所以下次调用rand
返回一个足够随机的数字。例如来自 http://linux.die.net/man/3/rand:
内部状态
next
被声明为全局状态。每个myrand
调用都会修改内部状态并更新它,并返回一个随机数。每次调用 myrand 都会有不同的 next 值,因此该方法每次调用都会返回不同的数字。查看
mysrand
实现;它只是设置您传递给next
的种子值。因此,如果您在调用rand
之前每次都将next
值设置为相同,它将返回相同的随机值,因为对其应用了相同的公式,这是不可取的,因为该函数是随机的。但根据您的需要,您可以将种子设置为某个特定值,以便每次运行生成相同的“随机序列”,例如某些基准测试或其他基准测试。
Random numbers are actually pseudo random. A seed is set first, from which each call of
rand
gets a random number, and modifies the internal state and this new state is used in the nextrand
call to get another number. Because a certain formula is used to generate these "random numbers" therefore setting a certain value of seed after every call torand
will return the same number from the call. For examplesrand (1234); rand ();
will return the same value. Initializing once the initial state with the seed value will generate enough random numbers as you do not set the internal state withsrand
, thus making the numbers more probable to be random.Generally we use the
time (NULL)
returned seconds value when initializing the seed value. Say thesrand (time (NULL));
is in a loop. Then loop can iterate more than once in one second, therefore the number of times the loop iterates inside the loop in a secondrand
call in the loop will return the same "random number", which is not desired. Initializing it once at program start will set the seed once, and each timerand
is called, a new number is generated and the internal state is modified, so the next callrand
returns a number which is random enough.For example this code from http://linux.die.net/man/3/rand:
The internal state
next
is declared as global. Eachmyrand
call will modify the internal state and update it, and return a random number. Every call ofmyrand
will have a differentnext
value therefore the the method will return the different numbers every call.Look at the
mysrand
implementation; it simply sets the seed value you pass tonext
. Therefore if you set thenext
value the same everytime before callingrand
it will return the same random value, because of the identical formula applied on it, which is not desirable, as the function is made to be random.But depending on your needs you can set the seed to some certain value to generate the same "random sequence" each run, say for some benchmark or others.
简短的回答:调用
srand()
不像就像随机数生成器的“掷骰子”一样。这也不像洗一副牌那样。如果有的话,那就更像是切一副牌。这样想吧。 rand() 处理一大堆牌,每次调用它时,它所做的就是从牌堆顶部挑选下一张牌,给你值,然后将该牌返回给甲板的底部。 (是的,这意味着“随机”序列会在一段时间后重复。不过,这是一个非常大的牌组:通常有 4,294,967,296 张牌。)
此外,每次程序运行时,都会出现一组全新的牌卡片是从游戏商店购买的,每副全新的卡片总是具有相同的顺序。因此,除非您做了一些特殊的事情,否则每次程序运行时,它都会从
rand()
返回完全相同的“随机”数字。现在,你可能会说,“好吧,那么我该如何洗牌呢?”答案——至少就
rand
和srand
而言——是没有办法洗牌。那么
srand
有什么作用呢?根据我在这里构建的类比,调用 srand(n) 基本上就像说“从顶部切下一副n
张牌”。但是等等,还有一件事:它实际上是从另一副全新的牌开始,然后从顶部切n
张牌。因此,如果您调用
srand(n)
、rand()
、srand(n)
、rand()
, ...,每次使用相同的n
时,您不仅会得到一个不是非常随机的序列,实际上还会从rand()
返回相同的数字代码> 每次。 (可能不是您交给srand
的数字,而是从rand
一遍又一遍返回的相同数字。)因此,您能做的最好的事情就是切牌once,即在程序开始时使用相当随机的
n
调用srand()
一次,这样您就可以开始每次都在大牌组的不同随机位置你的程序运行。使用rand()
,这确实是您能做的最好的事情。[PS是的,我知道,在现实生活中,当你购买一副全新的牌时,它通常是按顺序排列的,而不是随机排列的。为了让这里的类比起作用,我想象您从游戏商店购买的每副牌都是看似随机的顺序,但与您从同一家商店购买的所有其他牌组的看似随机的顺序完全相同。有点像他们在桥牌锦标赛中使用的相同洗牌的牌。]
附录:对于一个非常可爱的演示,说明对于给定的 PRNG 算法和给定的种子值,您总是会得到相同的序列,请参阅 这个问题(这是关于Java,而不是C,但是 反正)。
Short answer: calling
srand()
is not like "rolling the dice" for the random number generator. Nor is it like shuffling a deck of cards. If anything, it's more like just cutting a deck of cards.Think of it like this.
rand()
deals from a big deck of cards, and every time you call it, all it does is pick the next card off the top of the deck, give you the value, and return that card to the bottom of the deck. (Yes, that means the "random" sequence will repeat after a while. It's a very big deck, though: typically 4,294,967,296 cards.)Furthermore, every time your program runs, a brand-new pack of cards is bought from the game shop, and every brand-new pack of cards always has the same sequence. So unless you do something special, every time your program runs, it will get exactly the same "random" numbers back from
rand()
.Now, you might say, "Okay, so how do I shuffle the deck?" And the answer -- at least as far as
rand
andsrand
are concerned -- is that there is no way of shuffling the deck.So what does
srand
do? Based on the analogy I've been building here, callingsrand(n)
is basically like saying, "cut the deckn
cards from the top". But wait, one more thing: it's actually start with another brand-new deck and cut itn
cards from the top.So if you call
srand(n)
,rand()
,srand(n)
,rand()
, ..., with the samen
every time, you won't just get a not-very-random sequence, you'll actually get the same number back fromrand()
every time. (Probably not the same number you handed tosrand
, but the same number back fromrand
over and over.)So the best you can do is to cut the deck once, that is, call
srand()
once, at the beginning of your program, with ann
that's reasonably random, so that you'll start at a different random place in the big deck each time your program runs. Withrand()
, that really is the best you can do.[P.S. Yes, I know, in real life, when you buy a brand-new deck of cards it's typically in order, not in random order. For the analogy here to work, I'm imagining that each deck you buy from the game shop is in a seemingly random order, but the exact same seemingly-random order as every other deck of cards you buy from that same shop. Sort of like the identically shuffled decks of cards they use in bridge tournaments.]
Addendum: For a very cute demonstration of the fact that for a given PRNG algorithm and a given seed value, you always get the same sequence, see this question (which is about Java, not C, but anyway).
原因是 srand() 设置随机生成器的初始状态,并且如果您自己不触及中间的状态,则生成器生成的所有值都只是“足够随机”。
例如,您可以这样做:
然后,如果您重复调用该函数,以便
time()
在相邻调用中返回相同的值,您只会生成相同的值 - 这是设计使然。The reason is that
srand()
sets the initial state of the random generator, and all the values that generator produces are only "random enough" if you don't touch the state yourself in between.For example you could do:
and then if you call that function repeatedly so that
time()
returns the same values in adjacent calls you just get the same value generated - that's by design.一个更简单的解决方案是使用 srand() 为同一秒运行的应用程序实例生成不同的种子,如下所示。
此方法使您的种子非常接近随机,因为无法猜测您的线程何时启动,并且 pid 也会不同。
A simpler solution for using
srand()
for generating different seeds for application instances run at the same second is as seen.This method makes your seed very close to random as there is no way to guess at what time your thread started and the pid will be different also.
srand 为伪随机数生成器提供种子。如果您多次调用,您将重新为 RNG 播种。如果您使用相同的参数调用它,它将重新启动相同的序列。
为了证明这一点,如果您执行如下简单操作,您将看到相同的数字被打印 100 次:
srand seeds the pseudorandom number generator. If you call it more than once, you will reseed the RNG. And if you call it with the same argument, it will restart the same sequence.
To prove it, if you do something simple like this, you will see the same number printed 100 times:
似乎每次
rand()
运行时,它都会为下一个rand()
设置一个新的种子。如果
srand()
运行多次,问题是如果两次运行在一秒内发生(time(NULL)
不改变),则下一个< code>rand() 将与前一个srand()
之后的rand()
相同。It seems that every time
rand()
runs, it will set a new seed for the nextrand()
.If
srand()
runs multiple times, the problem is if the two running happen in one second (thetime(NULL)
does not change), the nextrand()
will be the same as therand()
right after the previoussrand()
.