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

发布于 2024-12-03 06:42:10 字数 196 浏览 1 评论 0原文

这个问题是关于这个问题的评论 初始化 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

薄荷→糖丶微凉 2024-12-10 06:42:10

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

随机化作为具有起始值(即种子)的函数来执行。

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

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

种子通常取自当前时间,即秒,如 time(NULL) 所示,因此如果您始终在获取随机数之前设置种子,那么只要您在同一秒内多次调用 srand/rand 组合。

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

但是,您有可能在一秒钟内多次运行您的应用程序(特别是如果它很短,或者是命令行工具或类似的东西),那么您将不得不诉诸其他方式来选择种子(除非您可以接受不同应用程序实例中的相同序列)。但正如我所说,这取决于您的应用程序使用环境。

另外,您可能想尝试将精度提高到微秒(最大限度地减少相同种子的机会),需要(sys/time.h):

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

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):

struct timeval t1;
gettimeofday(&t1, NULL);
srand(t1.tv_usec * t1.tv_sec);
俯瞰星空 2024-12-10 06:42:10

随机数实际上是伪随机的。首先设置一个种子,每次调用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 的种子值。因此,如果您在调用 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 next rand 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 to rand will return the same number from the call. For example srand (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 with srand, thus making the numbers more probable to be random.

Generally we use the time (NULL) returned seconds value when initializing the seed value. Say the srand (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 second rand 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 time rand is called, a new number is generated and the internal state is modified, so the next call rand returns a number which is random enough.

For example this code from 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;
}

The internal state next is declared as global. Each myrand call will modify the internal state and update it, and return a random number. Every call of myrand will have a different next 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 to next. Therefore if you set the next value the same everytime before calling rand 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.

缪败 2024-12-10 06:42:10

简短的回答:调用srand()不像就像随机数生成器的“掷骰子”一样。这也不像洗一副牌那样。如果有的话,那就更像是切一副牌。

这样想吧。 rand() 处理一大堆牌,每次调用它时,它所做的就是从牌堆顶部挑选下一张牌,给你值,然后将该牌返回给甲板的底部。 (是的,这意味着“随机”序列会在一段时间后重复。不过,这是一个非常大的牌组:通常有 4,294,967,296 张牌。)

此外,每次程序运行时,都会出现一组全新的牌卡片是从游戏商店购买的,每副全新的卡片总是具有相同的顺序。因此,除非您做了一些特殊的事情,否则每次程序运行时,它都会从 rand() 返回完全相同的“随机”数字。

现在,你可能会说,“好吧,那么我该如何洗牌呢?”答案——至少就 randsrand 而言——是没有办法洗牌。

那么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 and srand 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, calling srand(n) is basically like saying, "cut the deck n cards from the top". But wait, one more thing: it's actually start with another brand-new deck and cut it n cards from the top.

So if you call srand(n), rand(), srand(n), rand(), ..., with the same n every time, you won't just get a not-very-random sequence, you'll actually get the same number back from rand() every time. (Probably not the same number you handed to srand, but the same number back from rand 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 an n that's reasonably random, so that you'll start at a different random place in the big deck each time your program runs. With rand(), 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).

清浅ˋ旧时光 2024-12-10 06:42:10

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

例如,您可以这样做:

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

然后,如果您重复调用该函数,以便 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:

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

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.

我不是你的备胎 2024-12-10 06:42:10

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

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

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

A simpler solution for using srand() for generating different seeds for application instances run at the same second is as seen.

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

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.

那片花海 2024-12-10 06:42:10

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

为了证明这一点,如果您执行如下简单操作,您将看到相同的数字被打印 100 次:

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

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:

#include <stdlib.h>
#include <stdio.h>
int main() {
    for(int i = 0; i != 100; ++i) {
        srand(0);
        printf("%d\n", rand());
    }
}
北斗星光 2024-12-10 06:42:10
  1. 似乎每次 rand() 运行时,它都会为下一个 rand() 设置一个新的种子。

  2. 如果srand()运行多次,问题是如果两次运行在一秒内发生(time(NULL)不改变),则下一个< code>rand() 将与前一个 srand() 之后的 rand() 相同。

  1. It seems that every time rand() runs, it will set a new seed for the next rand().

  2. If srand() runs multiple times, the problem is if the two running happen in one second (the time(NULL) does not change), the next rand() will be the same as the rand() right after the previous srand().

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文