rand() 生成相同的数字 –即使在我的主程序中使用 srand(time(NULL)) !
所以,我试图创建一个随机向量(想想几何,而不是可扩展数组),每次调用随机向量函数时,我都会得到相同的 x 值,尽管 y 和 z 不同。
int main () {
srand ( (unsigned)time(NULL));
Vector<double> a;
a.randvec();
cout << a << endl;
return 0;
}
使用函数
//random Vector
template <class T>
void Vector<T>::randvec()
{
const int min=-10, max=10;
int randx, randy, randz;
const int bucket_size = RAND_MAX/(max-min);
do randx = (rand()/bucket_size)+min;
while (randx <= min && randx >= max);
x = randx;
do randy = (rand()/bucket_size)+min;
while (randy <= min && randy >= max);
y = randy;
do randz = (rand()/bucket_size)+min;
while (randz <= min && randz >= max);
z = randz;
}
出于某种原因,randx 将始终返回 8,而其他数字似乎完全遵循(伪)随机性。但是,如果我在 randx 之前调用定义(例如 randy),randy 将始终返回 8。
为什么我的第一个随机数始终是 8?难道是我播种方式不对?
So, I'm trying to create a random vector (think geometry, not an expandable array), and every time I call my random vector function I get the same x value, though y and z are different.
int main () {
srand ( (unsigned)time(NULL));
Vector<double> a;
a.randvec();
cout << a << endl;
return 0;
}
using the function
//random Vector
template <class T>
void Vector<T>::randvec()
{
const int min=-10, max=10;
int randx, randy, randz;
const int bucket_size = RAND_MAX/(max-min);
do randx = (rand()/bucket_size)+min;
while (randx <= min && randx >= max);
x = randx;
do randy = (rand()/bucket_size)+min;
while (randy <= min && randy >= max);
y = randy;
do randz = (rand()/bucket_size)+min;
while (randz <= min && randz >= max);
z = randz;
}
For some reason, randx will consistently return 8, whereas the other numbers seem to be following the (pseudo) randomness perfectly. However, if I put the call to define, say, randy before randx, randy will always return 8.
Why is my first random number always 8? Am I seeding incorrectly?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
我也有同样的问题。我通过移动 srand() 调用来修复它,因此它只在我的程序中调用一次(之前我一直在函数调用的顶部播种它)。
不太明白技术细节 - 但问题已经解决了。
I had the same problem exactly. I fixed it by moving the srand() call so it was only called once in my program (previously I had been seeding it at the top of a function call).
Don't really understand the technicalities - but it was problem solved.
另外值得一提的是,您甚至可以摆脱那个奇怪的
bucket_size
变量,并使用以下方法生成从a
到b
的数字包含:Also to mention, you can even get rid of that strange
bucket_size
variable and use the following method to generate numbers froma
tob
inclusively:一个简单的快速修复方法是在播种后调用
rand
几次。为了更好地解释,在测试程序的四次连续运行中第一次调用 rand() 给出了以下输出:
注意它们有多相似?例如,如果将
rand()
除以 100,您将连续 3 次得到相同的数字。现在看一下 rand() 在四次连续运行中的第二个结果:这看起来好多了,不是吗?我真的看不出有什么理由投反对票。
A simple quickfix is to call
rand
a few times after seeding.Just to explain better, the first call to rand() in four sequential runs of a test program gave the following output:
Notice how similar they are? For example, if you divide
rand()
by 100, you will get the same number 3 times in a row. Now take a look at the second result of rand() in four sequential runs:This looks much better, doesn't it? I really don't see any reason for the downvotes.
您的实现通过整数除法忽略随机数的最小 4-5 位。由于您的 RNG 是用系统时间作为种子的,因此您从中获得的第一个值只会(平均)每 20 秒改变一次。
这应该有效:
[0, 1) 中的随机双精度值在哪里
,其余的只是将其移动。
Your implementation, through integer division, ignores the smallest 4-5 bit of the random number. Since your RNG is seeded with the system time, the first value you get out of it will change only (on average) every 20 seconds.
This should work:
where
is a random double value in [0, 1) and the rest is just shifting it around.
与这个问题中的代码没有直接关系,但我在使用时遇到了同样的问题
srand ((unsigned)time(NULL))
并且仍然具有从以下对rand()
的调用返回的值的相同序列。事实证明, srand 需要分别调用您正在使用它的每个线程。我有一个正在生成随机内容的加载线程(由于种子问题,这不是随机的)。我只是在主线程中使用 srand 而不是加载线程。因此添加另一个 srand ((unsigned)time(NULL)) 来开始加载线程解决了这个问题。
Not directly related to the code in this question, but I had same issue with using
srand ((unsigned)time(NULL))
and still having same sequence of values being returned from following calls torand()
.It turned out that srand needs to called on each thread you are using it on separately. I had a loading thread that was generating random content (that wasn't random cuz of the seed issue). I had just using srand in the main thread and not the loading thread. So added another
srand ((unsigned)time(NULL))
to start of loading thread fixed this issue.播种后,为什么第一次调用
rand
总是生成相同的数字?需要调查三个潜在原因。一个或多个可能是促成因素。
rand
的糟糕实现rand
生成的值映射到所需输出范围的问题自从 OP 编写以来的 14 年里发生了很多变化2010年,当时C++11还处于草案阶段,
还没有被正式采用。单独的随机数引擎和随机数分布的概念尚未普及。OP 问道,“[为什么]
rand()
生成相同的数字 - 即使在我的main
中使用srand(time(NULL))
?”他的意思是,“为什么在函数randvec
中编码的随机数分布总是在播种rand
后的第一次调用时生成相同的数字>?”正如下面的数据所示,只要给它不同的种子,rand
,它本身确实生成至少有一点不同的数字。1.
rand
有问题吗?在评论中,OP @Nick Sweet 透露他在 2010 年使用的是 Xcode。交叉检查日期,这意味着他的系统运行的是 OS X。 根据 Wikipedia,Apple 当时对
rand
的实现是 CarbonLib。它使用了 Park 和 Miller 于 1988 年以 MINSTD 形式发布的乘法同余生成器 (MCG)。可以在标头
中找到相同的生成器,如std::minstd_rand0
。在 20 世纪 80 年代,当微处理器仍在使用 16 位字并且时钟速率以兆赫为单位时,MINSTD 占有一席之地。如今,它已经失宠了。除其他外,许多人认为 2^31 的周期短得令人无法接受。
是 MINSTD 的问题吗?为了找到答案,我测试了五个随机数引擎:
std::minstd_rand0
– MINSTD (Park & Miller, 1988)std::minstd_rand
– MINSTD (Park & Miller) , 1993)tbx::knuth_lcg
–pcg32
使用的 64 位 LCG(线性同余生成器)tbx::pcg32_engine
– 32 位 PCG (置换同余生成器)由 ME O'Neill 设计。它实现了 O'Neill 的pcg32_oneseq
。std::mt19937
– 32 位 Mersenne Twister 引擎tbx::pcg_engine
的源代码太长,无法包含在此处。它改编自ME O'Neill 发布的程序。然而,
tbx::knuth_lcg
的源代码非常短。由于其周期为 2^64 以及精心选择的参数,tbx::knuth.lcg
优于 MINSTD。2. std::time(NULL) 有多糟糕?
在许多系统上,
std::time(NULL)
返回的时间值具有一秒的分辨率。当快速运行的程序多次调用 srand(time(NULL)) 时,很有可能所有都将使用相同的种子!如果用作种子的时间值不相同,它们可能非常相似,可能仅相隔几秒。在这种情况下,如果 PRNG 不以某种方式条件种子来进一步随机化它,那么 PRNG 的初始输出可能是相似的。将发电机循环“少量”次数可以减轻这种播种的影响。
是种子的问题吗?为了找到答案,每个随机数引擎都使用两种播种方法进行了测试。测试以一秒的间隔运行。
std::srand(std::time(NULL))
std::srand(std::random_device{}());
std::random_device< /code> 是
标头的一部分。它生成 32 位无符号值,旨在用作种子。根据编译器的不同,它生成的值可以是在硬件中生成的真正随机值,也可以是通常由加密安全伪随机数生成器 (CSPRNG) 生成的高质量伪随机值。注意:
std::random_device
也有自己的一系列问题。特别是,9.2 版本之前的 GCC MinGW 发行版存在错误。这是报告。这些版本使用std::mt19937
和固定种子,并且std::random_device
每次都会生成相同的序列。3. 随机数分布有问题吗?
2010 年的许多评论者没有意识到函数
randvec
实现了拒绝采样算法,以生成均匀分布的随机值。他们中令人惊讶的数量建议放弃它使用的循环,转而支持由以下方式产生的有偏见的值:很难责怪他们。 OP 中的拒绝测试编码错误,使用的是
&&
,而不是||
。难怪人们看不到发生了什么。根据 OP,函数
randvec
改编自 Andrew Koenig & 出版于 Accelerated C++ (2000) 中的算法。芭芭拉·穆 (Barbara E. Moo)该算法在该书的第 7 章中以函数nrand
的形式实现。与上面的偏置技术相比,它使用商而不是余数将rand
返回的值映射到输出范围。因此,它对来自rand
的高阶位敏感,而不是低阶位。当rand
中的两个值共享相同的高位时,nrand
返回的值通常会相同。相反,当高位发生变化时,nrand
返回的值也会发生变化。它对低位的变化不太敏感。以下是 Accelerated C++ 的源代码:
nrand
的工作原理是将输入范围 [0, RAND_MAX] 划分为n
个大小相等的“桶”,编号为0
到n-1
。每个存储桶都包含与输入范围不同的bucket_size
值。只允许装满桶。如果 [0, RAND_MAX] 中存在未分配给任何存储桶的剩余值,则每当调用rand
生成其中一个值时,它们就会被拒绝。整数商
rand()/bucket_size
产生范围[0, n] 上的结果。在程序中,商称为r
,但您可以将其视为bucket_number
。当r
等于n
时,它被拒绝,程序循环返回,再次调用rand
。否则,r 是位于半开区间 [0, n) 上的“桶号”。注意:虽然
nrand
中的算法是正确的,但它返回的值位于半开区间[0, n)上。当使用n
等于RAND_MAX
调用nrand
时,它永远不会返回RAND_MAX
。请参阅下文,了解在nrand
中扩展算法的方法,以便可以返回RAND_MAX
。以下函数使用
nrand
在闭范围 [a, b] 上生成均匀分布的值。但请注意,您不能使用a
等于 0 且b
等于RAND_MAX
来调用它。如果这样做,nrand
的参数将为RAND_MAX + 1
,并且会发生以下两种情况之一:RAND_MAX
小于>INT_MAX
、nrand
将抛出异常。RAND_MAX + 1
的计算将溢出,并且将出现未定义的行为。给定这些函数,来自OP的函数
randvec
可以编码如下:nrand
是randvec
在之后的第一次调用失败的原因吗?播种?为了找到答案,我使用上面给出的两个随机数函数测试了 RNG 和种子的每种组合:biased_rand(-10, 10)
uniform_rand(-10, 10)
这里是
uniform_rand
的改进版本,可以返回RAND_MAX
。鉴于最好避免使用 std::rand ,这比其他任何事情都更令人好奇。但是,如果您确实选择使用rand
,则应考虑使用此函数。对于生产工作,您应该使用std::uniform_int_distribution
。那么,给我看看数据吧!
std::minstd_rand0
第一个表显示了 MINSTD 的输出。前五列包含种子
std::time(NULL)
的数据。最后四列将种子设置为std::random_device{}()
。在第 1 列和第 2 列中,每行时间值增加 1 秒。那是因为我将线程编程为在行之间休眠 1 秒。
在第 3 列中,
rand
返回的值非常相似。它们均以366
开头,第四位数字为3
、4
、5
或6
。对于 MINSTD,单独使用当前时间作为种子并不是一个好主意。第 4 列显示了播种后第一次调用时
uniform_rand
返回的值。它永远不会改变。 RNG和seed的这个组合是失败的。第 5 列显示
biased_rand
返回的值。注意第 5 列中的重复模式。这也是一个失败。现在我们已经得到了 OP 中两个问题的答案。
答案是: (1) 当对
std::time(NULL)
的紧密调用用作 MINSTD 的种子时,其第一次调用返回的结果不是随机的。 (2)是的。请参阅 ME 的 简单可移植 C++ 种子熵奥尼尔提出了一些关于更好播种的想法。否则,只需使用std::random_device{}()
。我未经检验的假设是加性常数(MINSTD 中为 0)是导致第 3-5 列失败的一个因素。相比之下,
tbx::knuth_lcg
做得更好。MINSTD 是一个乘法同余生成器 (MCG)。除了模数之外,MINSTD 中的状态转换函数只有一个参数:乘数。对于
tbx::knuth_lcg
,有两个:乘数和加法常数。这种组合意味着相似的种子在 tbx::knuth_lcg 的单个循环中比在 MINSTD 中分歧更多。至少,这是假设。
接下来的四列展示了 MINSTD 是可用的,但前提是您有更好的种子。
第 6 列显示了
std::random_device
返回的种子。有趣的是,它们看起来很好而且很随意。第一次调用rand
返回的值也是如此,如第 7 列所示。第 8 列和第 9 列显示函数
uniform_rand
和biased_rand< 生成的输出/代码>。有趣的是,这两列似乎都是随机的,但我们知道严格的测试会表明第 9 列中的值存在微小偏差。
std::minstd_rand
的数据(未显示)与上面的数据具有相同的失败(和成功)。tbx::knuth_lcg
tbx::knuth_lcg
表现优于 MINSTD。当面对一系列相似的种子(第 2 列)时,它会生成看起来像是随机输出的内容(据说是随机输出)(第 3 列)。然而,tbx::knuth_lcg 也有其自身的问题。肉眼看来,第 3 列中的值看起来是随机的,但第 4 列中的值引起了怀疑。重复次数不足。想想生日问题。
我们从 21 个总体中抽取了 20 个值,并且只有两个重复(0 和 -5)。第 8 列,播种效果更好,更令人满意。它有很多重复,甚至有几个三重。
在程序的多次运行(但不是全部)中,出现了相同的模式。尽管我只是使用“眼球”测试来评估数据,但这件事的发生足以让我担心。
当种子良好时,如第 6 列所示,
tbx::knuth_lcg
会产生良好的结果。std::mt19937
的Mersenne Twister数据一致良好。
它在播种不良的情况下表现良好的原因可能是因为它在将种子安装到引擎之前调节您提供的种子。
当您使用单个(无符号)整数为
std::mt19937
提供种子时,它会将该种子推送到种子序列,以填充引擎使用的 624 个状态变量。从外观上看,第 2 列中的种子看起来都很相似。然而,当它们撞击引擎时,它们已经被显着地“随机化”了。pcg_engine
(也调节其种子)的结果类似。结论
主要结论是
std::time(NULL)
是一个较差的随机种子源。当一个程序多次调用它时,它返回的种子很可能非常相似。在最坏的情况下(这并不罕见),种子将是相同的。当
std::time(NULL)
返回的值不相同时,它们在用作std::mt19937
和pcg_engine
的种子时起作用。然而,他们的其他引擎却失败了。当然,当这些值相同时,所有引擎都会失败。std::time(NULL)
仍然可以用作种子,但它应该与其他“熵”源结合起来。这可以通过将其与内存中某些对象的地址进行异或或调用std::random_device
(或两者)来完成。请参阅 简单可移植 C++ 种子熵,作者:我奥尼尔。否则,只需使用
std::random_device
来生成种子。尽管 std::rand 有着复杂的历史,但它可能不会像 std::time(NULL) 那样成为问题。当然,这取决于您的系统。即使使用低级 MINSTD 生成器实现 rand(如 OP 中所示),上述数据也表明正确的播种可以产生有用的结果。如果有选择,您应该更喜欢
std::mt19937
或pcg32_engine
之类的东西。然而,对于轻量级应用程序,rand
可能就可以了。至于函数
nrand
,随机数分布,这是一种垃圾输入,垃圾输出的情况。当您向其提供一系列相似的随机值(其中高位是恒定的)时,nrand
将会陷入困境。有趣的是,在std::minstd_rand0
和std::minstd_rand
的测试中,nrand
陷入困境的时刻也是biased_rand 也有自己的问题。
After seeding, why does the first call to
rand
always generate the same number?There are three potential causes to investigate. One or more may be contributing factors.
rand
rand
onto the desired output rangeA lot has changed in the 14 years since the OP was written in 2010. Back then, C++11 was still in the draft stage, and
<random>
had not yet been officially adopted. The concept of separate random number engines and random number distributions had not yet taken hold.The OP asks, "[Why is]
rand()
generating the same number – even withsrand(time(NULL))
in mymain
?" What he means is, "Why does the random number distribution coded in functionrandvec
always generate the same number, on the first call, after seedingrand
?" As is shown in the data below, so long as it is given different seeds,rand
, itself, does generate numbers that are, at least, a little bit different.1. Are there problems with
rand
?In the comments, the OP, @Nick Sweet, revealed that he was using Xcode in 2010. Cross-checking dates, that means his system was running OS X. According to Wikipedia, Apple's implementation of
rand
at that time was part of CarbonLib. It used the multiplicative congruential generator (MCG) published as MINSTD, in 1988, by Park and Miller. The same generator can be found in header<random>
, asstd::minstd_rand0
.In the 1980s, when microprocessors were still using 16-bit words, and clock rates were measured in megahertz, MINSTD had a place. Today, it has fallen out of favor. Among other things, its period of 2^31 is considered by many to be unacceptably short.
Is MINSTD the problem? To find out, I tested five random number engines:
std::minstd_rand0
– MINSTD (Park & Miller, 1988)std::minstd_rand
– MINSTD (Park & Miller, 1993)tbx::knuth_lcg
– 64-bit LCG (linear congruential generator) used bypcg32
tbx::pcg32_engine
– 32-bit PCG (permuted congruential generator) designed by M.E. O'Neill. It implements O'Neill'spcg32_oneseq
.std::mt19937
– 32-bit Mersenne Twister engineSource code for
tbx::pcg_engine
is too long to be included here. It was adapted from the program published by M.E. O'Neill.Source code for
tbx::knuth_lcg
, however, is quite short. Due to its period of 2^64, and its well-chosen parameters,tbx::knuth.lcg
is superior to MINSTD.2. How bad is
std::time(NULL)
?On many systems, the time values returned by
std::time(NULL)
have a one-second resolution. When a fast-running program makes several calls tosrand(time(NULL))
, there is a significant chance that all of them will use the same seed!If the time values used as seeds are not identical, they may be very similar, perhaps separated by only a few seconds. In that case, if the PRNG does not condition the seed in some way, to further randomize it, then the initial outputs from the PRNG could be similar. Cycling the generator a "small" number of times can lessen the effect of such a seeding.
Is the seed the problem? To find out, each random number engine was tested with two seeding methods. Tests were run at one-second intervals.
std::srand(std::time(NULL))
std::srand(std::random_device{}());
std::random_device
is part of the<random>
header. It generates 32-bit unsigned values that are intended to be used as seeds. Depending on the compiler, the values it generates can be truly random values, generated in hardware, or high-quality, pseudorandom values, often generated by a cryptographically secure pseudorandom number generator (CSPRNG).Note:
std::random_device
comes with its own set of issues. In particular, MinGW distributions of GCC, prior to version 9.2, were buggy. Here is the report. Those versions usedstd::mt19937
with a fixed seed, andstd::random_device
generated the same sequence, every time.3. Are there problems with the random number distribution?
Many of the commenters from 2010 did not recognize that function
randvec
implements a rejection sampling algorithm, to generate uniformly distributed random values. A surprising number of them recommend ditching the loops that it uses, in favor of the biased values produced by:It's hard to blame them. The rejection tests in the OP were miscoded, using
&&
, instead of||
. No wonder folks couldn't see what was happening.According to the OP, function
randvec
was adapted from an algorithm published in Accelerated C++ (2000), by Andrew Koenig & Barbara E. Moo. The algorithm was implemented as functionnrand
, in Chapter 7 of that book. Compared to the biased technique above, it uses a quotient, rather than a remainder, to map the values returned byrand
onto the output range. Because of this, it is sensitive to the high-order bits coming fromrand
, rather than the low-order bits. When two values fromrand
share the same high-order bits, the values returned bynrand
will often be the same. Conversely, when the high-order bits vary, so will the values returned bynrand
. It is less sensitive to changes in the low-order bits.Here is the source code from Accelerated C++:
nrand
works by dividing the input range [0, RAND_MAX] inton
equally sized "buckets," numbered0
ton-1
. Each bucket containsbucket_size
distinct values from the input range. Only full buckets are allowed. If there are leftover values from [0, RAND_MAX], which are not assigned to any bucket, they are rejected, whenever one of them is produced by a call torand
.The integer quotient
rand() / bucket_size
produces a result on the range [0, n]. In the program, the quotient is calledr
, but you can think of it asbucket_number
. Whenr
equalsn
, it is rejected, and the program loops back, to callrand
again. Otherwise,r
is a "bucket number" that lies on the half-open interval [0, n).Note: Although the algorithm in
nrand
is correct, the values it returns lie on the half-open interval [0, n). Whennrand
is invoked withn
equalsRAND_MAX
, it can never returnRAND_MAX
. See below for a way to extend the algorithm innrand
, so thatRAND_MAX
can be returned.The following function uses
nrand
to generate uniformly distributed values on the closed range [a, b]. Note, however, that you cannot call it witha
equals 0, andb
equalsRAND_MAX
. If you do, the argument tonrand
will beRAND_MAX + 1
, and one of two things will happen:RAND_MAX
is less thanINT_MAX
,nrand
will throw an exception.RAND_MAX + 1
will overflow, and undefined behavior will follow.Given these functions, function
randvec
, from the OP, can be coded as follows:Is
nrand
the reason whyrandvec
fails on the first call after seeding? To find out, I tested each combination of RNG and seed with the two random number functions given above:biased_rand(-10, 10)
uniform_rand(-10, 10)
Here is an improved version of
uniform_rand
, which can returnRAND_MAX
. Given thatstd::rand
is best avoided, it is more of a curiosity than anything else. If, however, you do choose to userand
, you should consider using this function. For production work, you should usestd::uniform_int_distribution
, instead.So, show me the data!
std::minstd_rand0
This first table shows the output from MINSTD. The first five columns contain data for the seed
std::time(NULL)
. The final four columns, have the seed set tostd::random_device{}()
.In columns 1 and 2, the time values increase by 1 second for each row. That's because I programmed the thread to sleep for 1 second between rows.
In column 3, the values returned by
rand
are very similar. They all begin with366
, and the fourth digit is either3
,4
,5
or6
. With MINSTD, using the current time, alone, as a seed, is not a good idea.Columns 4 shows the values returned by
uniform_rand
on the first call after seeding. It never changes. This combination of RNG and seed is a failure.Column 5 shows the values returned by
biased_rand
. Note the repeating pattern in column 5. This is also a failure.We now have the answers to the two questions in the OP.
The answers are: (1) When closely spaced calls to
std::time(NULL)
are used as seeds for MINSTD, the results returned by its first invocation are less than random. (2) Yes. See Simple Portable C++ Seed Entropy by M.E. O'Neill for some ideas on a better seeding. Otherwise, just usestd::random_device{}()
.My untested hypothesis is the the additive constant, which is 0 in MINSTD, is a contributing factor in the failures in columns 3-5. Comparitively,
tbx::knuth_lcg
did a better job.MINSTD is a multiplicative congruential generator (MCG). In addition to its modulus, the state transition function in MINSTD has only one parameter: its multiplier. With
tbx::knuth_lcg
, there are two: a multiplier and an additive constant. The combination means that similar seeds diverge more in a single cycle oftbx::knuth_lcg
than they do in MINSTD.At least, that's the hypothesis.
The next four columns demonstrate that MINSTD is usable, but only if you have a better seed.
Column 6 shows the seeds returned by
std::random_device
. Anecdotally, they appear to be nice and random. So do the values returned from the first call torand
, which are shown in column 7.Columns 8 and 9 show the output generated by functions
uniform_rand
andbiased_rand
. Anecdotally, both columns appear to be random, but we know that rigorous testing would reveal that the values in column 9 have a tiny bias.Data for
std::minstd_rand
(not shown), have the same failings (and successes) as the data above.tbx::knuth_lcg
tbx::knuth_lcg
fared better than MINSTD. When faced with a series of similar seeds (column 2), it generated what appears, anecdotally, to be random output (column 3).tbx::knuth_lcg
, however, has its own problems. To the naked eye, the values in column 3 look random, but the values in column 4 raise suspicions. There are not enough repeats. Think of the Birthday Problem.We drew 20 values, from a population of 21, and there were only two repeats (0 and -5). Column 8, where the seeding is better, is more satisfying. It has many repeats, and even a couple of triples.
In many runs of the program (but not all), the same pattern appeared. It happened enough to concern me, even though I am just using the "eyeball" test to evaluate the data.
When the seeds are good, as in column 6,
tbx::knuth_lcg
yields good results.Mersenne Twister
Data for
std::mt19937
were uniformly good.The reason it does well with poor seeding is likely due to the fact that it conditions the seeds you give it, before installing them in the engine.
When you seed
std::mt19937
with a single (unsigned) integer, it pushes that seed through a seed sequence, to fill the 624 state variables used by the engine. On the outside, the seeds in column 2 all look similar. By the time they hit the engine, however, they have been significantly "randomized."Results for
pcg_engine
, which also conditions its seeds, were similar.Conclusions
The primary conclusion is that
std::time(NULL)
is a poor source of random seeds. When a program makes several calls to it, the odds are good that the seeds it returns will be very similar. In the worst case, which is not uncommon, the seeds will be identical.When the values returned by
std::time(NULL)
were not identical, they worked when used as seeds forstd::mt19937
andpcg_engine
. They failed, however, with the other engines. When the values were identical, of course, they failed for all engines.std::time(NULL)
can still be used as a seed, but it should be combined with other sources of "entropy." This can be accomplished by exclusive-or'ing it with the addresses of certain objects in memory or with a call tostd::random_device
(or both). See Simple Portable C++ Seed Entropy, by M.E. O'Neill.Otherwise, just use
std::random_device
to generate your seeds.Although it has a checkered history,
std::rand
might not be as much of a problem asstd::time(NULL)
. That depends, of course, on your system. Even whenrand
is implemented using the lowly MINSTD generator, as in the OP, the foregoing data show that proper seeding can yield serviceable results. Given a choice, you should prefer something likestd::mt19937
orpcg32_engine
. For lightweight applications, however,rand
may be fine.As for function
nrand
, the random number distribution, that's a case of garbage-in, garbage-out. When you feed it a series of similar random values, in which the high-order bits are constant, it is to be expected thatnrand
will struggle. Interestingly, in tests of bothstd::minstd_rand0
andstd::minstd_rand
, the times whennrand
struggled were also the times whenbiased_rand
had problems of its own.问题是随机数生成器使用非常接近的值进行播种 - 程序的每次运行只会少量更改 time() 的返回值 - 可能是 1 秒,甚至可能没有!然后,相当差的标准随机数生成器使用这些相似的种子值来生成明显相同的初始随机数。基本上,您需要一个比 time() 更好的初始种子生成器和一个比 rand() 更好的随机数生成器。
我认为实际使用的循环算法是从 Accelerated C++ 中提取出来的,其目的是在所需范围内产生比使用 mod 运算符更好的数字分布。但它无法弥补总是(有效地)给予相同的种子。
The issue is that the random number generator is being seeded with a values that are very close together - each run of the program only changes the return value of time() by a small amount - maybe 1 second, maybe even none! The rather poor standard random number generator then uses these similar seed values to generate apparently identical initial random numbers. Basically, you need a better initial seed generator than time() and a better random number generator than rand().
The actual looping algorithm used is I think lifted from Accelerated C++ and is intended to produce a better spread of numbers over the required range than say using the mod operator would. But it can't compensate for always being (effectively) given the same seed.
我没有发现您的
srand()
有任何问题,并且当我尝试运行极其相似的代码时,我没有在第一个rand()
中重复获得相同的数字。然而,我确实注意到了另一个可能的问题。该行可能没有达到您的预期。只要
min < max
(而且总是应该如此),randx
不可能既小于或等于min
又大于或等于max
。另外,您根本不需要循环。相反,您可以使用以下方法获取最小值和最大值之间的值:I don't see any problem with your
srand()
, and when I tried running extremely similar code, I did not repeatedly get the same number with the firstrand()
. However, I did notice another possible issue.This line probably does not do what you intended. As long as
min < max
(and it always should be), it's impossible forrandx
to be both less than or equal tomin
and greater than or equal tomax
. Plus, you don't need to loop at all. Instead, you can get a value in between min and max using: