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

srand() — why call it only once?

提问人:Lipika Deka 提问时间:9/8/2011 最后编辑:Jonathan LefflerLipika Deka 更新时间:4/22/2022 访问量:90604

问:

这个问题是关于这个问题中的一个评论 推荐的初始化 srand 的方法?第一条评论说,在应用程序中应该只调用一次。为什么会这样?srand()

c 随机 SRAND

评论

0赞 Foo Bah 9/8/2011
尝试在循环中调用 SRAND,然后调用 RAND
11赞 Jonathan Leffler 1/3/2016
另见 Dilbert 的会计之旅
1赞 Jonathan Leffler 4/22/2022
参见 初始化 srand() 的推荐方法?使用 for 传递给的值过于简单,因此可能足够,因为通常也很简单(参见 C 标准中的示例实现,该示例假设 RAND_MAX 为 32,767)。链接的 Q&A 中的一个解决方案使用函数混合 3 个值 — 、 和 的结果。使用混合功能是个好主意。也可以使用CRC。time(0)srand()srand()clock()time(0)getpid()

答:

8赞 sharptooth 9/8/2011 #1

原因是设置了随机生成器的初始状态,并且生成器生成的所有值只有在您自己不触及两者之间的状态时才“足够随机”。srand()

例如,您可以执行以下操作:

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

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

131赞 Kornelije Petak 9/8/2011 #2

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

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

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

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

种子通常从当前时间开始,即秒,如 ,所以如果你总是在取随机数之前设置种子,只要你在同一秒内多次调用 srand/rand 组合,你就会得到相同的数字。time(NULL)

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

但是,您很有可能在一秒钟内多次运行您的应用程序(特别是如果它是一个简短的应用程序,或者命令行工具或类似的东西),那么您将不得不求助于其他方式来选择种子(除非您可以在不同的应用程序实例中使用相同的序列)。但就像我说的,这取决于你的应用程序使用上下文。

此外,您可能希望尝试将精度提高到微秒(最小化相同种子的几率),需要 ():sys/time.h

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

评论

4赞 Shahbaz 4/7/2013
旁注:在 POSIX 2008 中已过时。相反,它引入了可能需要与 链接的内容。不过,它可能尚未在许多平台上提供。在 Linux 中,这没关系。在 Mac 上,我认为它尚不可用。在 Windows 中,它可能永远不会可用。gettimeofdayclock_gettime-lrt
1赞 Jiminion 4/26/2017
t1.tv_usec 是一个长整数,而 srand 将一个无符号整数作为输入(我刚刚遇到了一个问题,它会产生影响。
0赞 Beezer 12/1/2020
这成功了。通过提高精度,它摆脱了我的重复项。非常感谢。我有一个最后期限要交付,这节省了我的德里埃。
3赞 Foo Bah 9/8/2011 #3

SRAND 为伪随机数生成器提供种子。如果多次调用它,则会重新设定 RNG 的种子。如果你用相同的参数调用它,它将重新启动相同的序列。

为了证明这一点,如果你做这样简单的事情,你会看到相同的数字被打印了 100 次:

#include <stdlib.h>
#include <stdio.h>
int main() {
    for(int i = 0; i != 100; ++i) {
        srand(0);
        printf("%d\n", rand());
    }
}
26赞 phoxis 9/8/2011 #4

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

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

例如,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;
}

内部状态声明为全局状态。每次调用都会修改内部状态并对其进行更新,并返回一个随机数。每次调用都会有不同的值,因此该方法每次调用都会返回不同的数字。nextmyrandmyrandnext

看实现;它只是设置您传递给的种子值。因此,如果每次在调用之前都设置相同的值,它将返回相同的随机值,因为对其应用了相同的公式,这是不可取的,因为该函数是随机的。mysrandnextnextrand

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

评论

0赞 Jiminion 4/26/2017
你不是说 mysrand() 的参数的(无符号长种子)吗?
0赞 phoxis 5/8/2017
@Jiminion 这是来自 的代码片段。范围是从 0 到 32767(假设为 RAND_MAX),这远小于范围。状态变量是由于内部乘法和加法将超出 的范围。之后,在上述指定范围内对结果进行缩放或修改。虽然你可以制造种子.man srandlongnextlongunsigned intlong
1赞 Jonathan Leffler 9/7/2017
请注意,C 标准也包括所示的代码片段。
3赞 achoora 1/11/2016 #5

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

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

这种方法使你的种子非常接近随机,因为没有办法猜测你的线程在什么时候开始,而且pid也会不同。

19赞 Steve Summit 10/26/2016 #6

简短的回答:对于随机数生成器来说,叫车不像“掷骰子”。这也不像洗一副牌。如果有的话,这更像是切一副牌。srand()

可以这样想。 从一大副牌中发牌,每次你叫它时,它所做的就是从一副牌的顶部挑选下一张牌,给你价值,然后将那张牌放回一副牌的底部。(是的,这意味着“随机”序列将在一段时间后重复。不过,这是一副非常大的牌组:通常有 4,294,967,296 张牌。rand()

此外,每次程序运行时,都会从游戏商店购买一包全新的卡牌,并且每包全新的卡牌总是具有相同的顺序。因此,除非你做一些特别的事情,否则每次你的程序运行时,它都会得到完全相同的“随机”数字。rand()

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

那怎么办呢?根据我在这里构建的类比,呼叫基本上就像说,“从顶部切掉牌”。但是等等,还有一件事:它实际上是从另一副全新的牌组开始,然后从顶部切开n张牌srandsrand(n)n

因此,如果你每次都调用相同的 , , , , ...,你不仅会得到一个不是很随机的序列,你实际上每次都会得到相同的数字。(可能不是你交给的同一个号码,而是一遍又一遍地回来的同一个号码。srand(n)rand()srand(n)rand()nrand()srandrand

因此,你能做的最好的事情就是在程序开始时剪掉一次卡组,也就是说,调用一次,使用一个相当随机的卡组,这样每次程序运行时,你都会从大卡组中的不同随机位置开始。有了,这真的是你能做的最好的事情。srand()nrand()

[附言是的,我知道,在现实生活中,当你购买一副全新的纸牌时,它通常是按顺序排列的,而不是随机排列的。为了在这里进行类比,我想象您从游戏商店购买的每副牌都是以看似随机的顺序排列的,但与您从同一家商店购买的所有其他牌组完全相同,看似随机的顺序完全相同。有点像他们在桥牌锦标赛中使用的一副洗牌。


附录:对于给定的 PRNG 算法和给定的种子值,您总是得到相同的序列,请参阅此问题(这是关于 Java,而不是 C,但无论如何)。

0赞 imoc 12/2/2017 #7
  1. 似乎每次运行,都会为下一次播下新的种子。rand()rand()

  2. 如果运行多次,问题是如果两次运行发生在一秒钟内(没有变化),那么下一次将与前一次相同。srand()time(NULL)rand()rand()srand()

评论

0赞 King Thrushbeard 12/2/2017
要点是,使用相同的种子进行多次初始化将导致 返回相同的值。srand()rand()