2009-06-10 118 views
5

我见过很多关于每次执行不超过一次伪随机数生成器的建议,但从来没有伴随彻底的解释。当然,很容易看出为什么以下(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

+0

我想补充一点这个练习的整个目的是缓解由`MyRand`,其中`MyRand`可能被造型模具的客户端调用函数srand的“包袱”。但另一方面,如果我们也以相同的方式制造幸运轮,抛硬币等,我们将获得很多种子。 – a038c56f 2009-06-10 18:42:06

回答

6

每次调用伪随机数生成器函数时,生成器都会采用一些内部状态并生成一个伪随机数和一个新的内部状态。仔细选择用于转换内部状态的算法,以使输出看起来是随机的。

当你种下随机数发生器时,你基本上就是设置这个内部状态。如果您将内部状态重置为某个可预测的值,您将失去随机性。例如,一个流行的,简单的RNG是线性同余发生器。数字生成像这样:

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

在这种情况下,X [n + 1]既是结果又是新的内部状态。如果你每次播种发生器你上面建议,你会得到一个序列,看起来像这样:

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

其中B是你的seed_base。这看起来并不随意。

+0

我的main.cpp例子有点夸张,目的是证明缺少第一个例子的缺陷。为每个调用get_rand实例化一个新对象将导致上面描述的情况,但这只是浪费编程。假设与`get_rand`调用相比,`MyRand`实例化的数量很少,事情看起来好一点。 – a038c56f 2009-06-10 18:31:40

1

如果你的种子是可预测的,它在这里,因为你只是递增它,rand()的输出也是可以预测的。

这真的取决于你为什么要产生随机数,以及“随机”对你来说是多么可接受的随机数。在你的例子中,它可能会避免连续的重复,这对你来说可能是足够好的。毕竟,重要的是它的运行。

在几乎每个平台上,都有一个比rand()更好的生成随机数的方法。

1

那么它是额外的处理,不需要做。

在这种情况下,我只是在循环开始之前用基于时间的种子调用构造函数一次。这将保证随机结果没有每次迭代改变种子的额外开销。

我不会认为你的方法是任何更多随机比。

0

您可以将随机数生成(这不是严格意义上的实现方式,但作为一个例子)作为值表。如果你记得在统计学中做这些简单的随机样本,种子基本上会告诉你在你的大表中随机数字开始的行和列。因为我们已经可以假设这些数字已经正常分布了,所以一遍又一遍地重新调整是不必要的。

由于这应该足够好(取决于应用程序),所以根本没有增加种子播种的好处。如果你确实需要“更多”的随机数,有很多随机数生成的方法。我能想到的一种情况是以线程安全的方式生成随机数。

虽然您的解决方案是可以接受的,但您的数字不会比在全球范围内播种一次更随机。 srand通常不应该属于构造函数。如果您想支持随机数字,请在程序启动时种一次,然后忘记它。