简体   繁体   English

不止一次播种伪随机数发生器的问题?

[英]Issues with seeding a pseudo-random number generator more than once?

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: 当然,很容易理解为什么以下(C / C ++)示例不是一个好主意:

int get_rand() {
  srand(time(NULL));
  return rand();
}

since calling get_rand several times per second produces repeated results. 因为每秒多次调用get_rand会产生重复的结果。

But wouldn't the following example still be an acceptable solution? 但是下面的例子不是一个可以接受的解决方案吗?

MyRand.h 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 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 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() << " ";
  }
}

ie even though MyRand :s constructor is called several times in rapid succession, each call to srand has a different parameter. 即使MyRand :s构造函数快速连续多次调用,每次调用srand都有不同的参数。 Obviously, this is not thread-safe, but then again neither is rand . 显然,这不是线程安全的,但是也不是rand

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. 例如,流行的简单RNG是线性同余生成器。 Numbers are generated like this: 数字生成如下:

X[n+1] = (a X[n] + c) mod m

In this case, X[n+1] is both the result and the new internal state. 在这种情况下,X [n + 1]既是结果又是新的内部状态。 If you seed the generator every time as you suggest above, you'll get a sequence that looks like this: 如果您按照上面的建议每次播种生成器,您将获得如下所示的序列:

{(ab + c) mod m, (a(b+1) + c) mod m, (a(b+2) + c) mod m, ...}

where b is your seed_base . 其中b是你的seed_base This doesn't look random at all. 这根本不是随机的。

If your seed is predictable, which it is here since you're just incrementing it, the output from rand() will also be predictable. 如果你的种子是可预测的,它就在这里,因为你只是递增它,rand()的输出也是可预测的。

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(). 几乎在每个平台上都有比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. 我不认为你的方法比那随机。

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. srand一般不应该属于构造函数。 If you'd like to support random numbers, seed once when the program starts, and forget about it. 如果您想支持随机数,请在程序启动时播种一次,然后忘记它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM