多次播种伪随机数生成器有问题吗?
我见过很多关于每次执行时不要多次播种伪随机数生成器的建议,但从未附有完整的解释。 当然,很容易看出为什么以下 (C/C++) 示例不是一个好主意:
int get_rand() {
srand(time(NULL));
return rand();
}
因为每秒多次调用 get_rand
会产生重复的结果。
但是下面的示例难道不是一个可以接受的解决方案吗?
MyRand.h
#ifndef MY_RAND_H
#define MY_RAND_H
class MyRand
{
public:
MyRand();
int get_rand() const;
private:
static unsigned int seed_base;
};
#endif
MyRand.cpp
#include <ctime>
#include <cstdlib>
#include "MyRand.h"
unsigned int MyRand::seed_base = static_cast<unsigned int>(time(NULL));
MyRand::MyRand()
{
srand(seed_base++);
}
int MyRand::get_rand() const
{
return rand();
}
main.cpp
#include <iostream>
#include "MyRand.h"
int main(int argc, char *argv[])
{
for (int i = 0; i < 100; i++)
{
MyRand r;
std::cout << r.get_rand() << " ";
}
}
即,尽管 MyRand
:s 构造函数被快速连续调用多次,但对 srand
的每次调用都有不同的参数。 显然,这不是线程安全的,但 rand 也不是线程安全的。
I've seen quite a few recommendations for not seeding pseudo-random number generators more than once per execution, but never accompanied by a thorough explanation. Of course, it is easy to see why the following (C/C++) example is not a good idea:
int get_rand() {
srand(time(NULL));
return rand();
}
since calling get_rand
several times per second produces repeated results.
But wouldn't the following example still be an acceptable solution?
MyRand.h
#ifndef MY_RAND_H
#define MY_RAND_H
class MyRand
{
public:
MyRand();
int get_rand() const;
private:
static unsigned int seed_base;
};
#endif
MyRand.cpp
#include <ctime>
#include <cstdlib>
#include "MyRand.h"
unsigned int MyRand::seed_base = static_cast<unsigned int>(time(NULL));
MyRand::MyRand()
{
srand(seed_base++);
}
int MyRand::get_rand() const
{
return rand();
}
main.cpp
#include <iostream>
#include "MyRand.h"
int main(int argc, char *argv[])
{
for (int i = 0; i < 100; i++)
{
MyRand r;
std::cout << r.get_rand() << " ";
}
}
i.e. even though MyRand
:s constructor is called several times in rapid succession, each call to srand
has a different parameter. Obviously, this is not thread-safe, but then again neither is rand
.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
每次调用伪随机数生成器函数时,生成器都会获取一些内部状态并生成一个伪随机数和一个新的内部状态。 用于转换内部状态的算法是经过仔细选择的,因此输出看起来是随机的。
当您为随机数生成器播种时,您基本上是在设置此内部状态。 如果将内部状态重置为某个可预测的值,您将失去随机性的外观。
例如,一种流行的简单 RNG 是线性同余生成器。 数字是这样生成的:
在这种情况下,X[n+1] 既是结果又是新的内部状态。 如果您每次都按照上面的建议为生成器播种,您将得到一个如下所示的序列:
其中 b 是您的
seed_base
。 这看起来根本不是随机的。Each time you call a pseudo-random number generator function, the generator takes some internal state and produces a pseudo-random number and a new internal state. The algorithm for transforming the internal state is carefully chosen so the output appears random.
When you seed the random number generator, you're basically setting this internal state. If you reset the internal state to some predictable value, you'll lose the appearance of randomness.
For example, a popular, simple RNG is a linear congruential generator. Numbers are generated like this:
In this case, X[n+1] is both the result and the new internal state. If you seed the generator every time as you suggest above, you'll get a sequence that looks like this:
where b is your
seed_base
. This doesn't look random at all.如果你的种子是可预测的(因为你只是递增它,所以它就在这里),那么 rand() 的输出也将是可预测的。
这实际上取决于您为什么要生成随机数,以及“随机”对您来说是可以接受的随机数。 在您的示例中,它可能会避免快速连续的重复,这对您来说可能就足够了。 毕竟,重要的是它能运行。
几乎在每个平台上都有比 rand() 更好的生成随机数的方法。
If your seed is predictable, which it is here since you're just incrementing it, the output from rand() will also be predictable.
It really depends on why you want to generate random numbers, and how "random" is an acceptable random for you. In your example, it may avoid duplicates in rapid succession, and that may be good enough for you. After all, what matters is that it runs.
On almost every platform there is a better way to generate random numbers than rand().
嗯,这是不需要完成的额外处理。
在这种情况下,我只需在循环开始之前使用基于时间的种子调用构造函数一次。 这将保证随机结果,而无需为每次迭代更改种子的额外开销。
我不认为你的方法比这更随机。
Well it's extra processing that doesn't need to be done.
In that scenario I'd just call the constructor once with a time-based seed before the start of the loop. That will guarantee random results without the extra overhead of changing seeds for every iteration.
I wouldn't think your method is any more random than that.
您可以将随机数生成(这不再是严格意义上的实现方式,而是作为说明)视为值表。 如果您记得在统计中做任何这些事情来进行简单的随机样本,那么种子基本上会告诉您在随机数大表中从哪一行和哪一列开始。 一遍又一遍地重新播种是完全没有必要的,因为我们已经可以假设这些数字已经呈正态分布。
多次播种根本没有额外的好处,因为这应该足够好(取决于应用程序)。 如果您确实需要“更多”随机数,有许多随机数生成方法。 我能想到的一种情况是以线程安全的方式生成随机数。
虽然您的解决方案是可以接受的,但您的数字不会比在全球范围内播种一次更随机。 srand 通常不应该属于构造函数。 如果您想支持随机数,请在程序启动时播种一次,然后就不用管它了。
You can think of random number generation (this is not strictly true implementation-wise any more, but serves as an illustration) as a table of values. If you remember doing any of this stuff in statistics for doing simple random samples, a seed basically tells you what row and column to start at in your big table of random numbers. Reseeding over and over again is simply unnecessary since we can already assume that the numbers are normally distributed already.
There is simply no added benefit to seeding more than once since this should be good enough (depending on the application). If you do need "more" random numbers, there are many methods of random number generation. One case that I can think of is to generate random numbers in a thread-safe manner.
While your solution is acceptable, your numbers will be no more random than seeding it once, globally. srand generally shouldn't belong in a constructor. If you'd like to support random numbers, seed once when the program starts, and forget about it.