【问题标题】:Sequential Random Numbers in C++?C ++中的顺序随机数?
【发布时间】:2013-08-19 12:04:53
【问题描述】:

我正在寻找一种在 C++ 中快速生成两个随机数的方法。该代码用于骰子,滚动时需要生成一个随机数。我的代码目前如下所示:

Die::Die() {
    srand(time(NULL));
}

void Die::roll() {
    value = rand() % 6 + 1;
}

如果我使用此代码创建两个对象,srand() 作为一个静态(非基于实例)函数,将为所有对象生成一个新种子。我也试过这样做:

void Die::roll() {
    srand(time(NULL));
    value = rand() % 6 + 1;
}

而不是将它放在构造函数中,但如果我像下面这样快速调用它们:

die0->roll();
die1->roll();

它们的值通常相等。有什么方法可以让这个随机的,每次?感谢你的帮助。 :)

【问题讨论】:

  • 您只需为随机数生成器播种一次。您可以通过将调用 srand(time(NULL)) 移动到初始化您的应用程序的代码轻松地做到这一点
  • @simonc 事实上,他应该只播种一次。就像现在一样,如果快速连续调用多次,它将一遍又一遍地以相同的值重新播种。
  • 您可能想查看new PRNG functionality of C++11,尤其是std::uniform_int_distribution 类和该链接中的示例。
  • @JoachimPileborg 是的,它还避免了模偏差。

标签: c++ random numbers


【解决方案1】:

假设程序运行得足够快,时间(以秒为单位)通常是相同的,这意味着您的种子将是相同的,这意味着您的随机值是相同的。 正如评论者所说,在其他地方做随机种子(比如你的程序或线程的开始)。

【讨论】:

  • 如果我这样做,两个 Die 实例的值是完全相同的,这就是为什么我首先尝试重新播种。
  • 没问题;你不需要重新播种。
  • 两个 Die 实例将是相同的,但随机数生成器会生成一个伪随机数字串(两个 Die 将从同一随机串中提取数字)。
  • 是的,我想通了,问题是我在 .cpp 文件的顶部有 value 变量,而不是作为头文件中的私有变量;这就是为什么价值观是一样的。我也将 srand() 移到了程序的开头。仍然习惯于 C++ 中的 OOP。 :) 感谢您的所有帮助。
【解决方案2】:

问题是,如果 srands 在时间上彼此足够接近,那么您正在为 rng 播种相同的值,因此在每个种子之后获得的第一个值将是相同的。为避免这种情况,您可以确保播种只发生一次。例如,您可以从 Die 类中删除 srand,并在第一次使用 Die 之前的某个时间播种。

或者,如果您不希望 Die 的使用者承担调用 srand 的负担,您可以将 srand 保留在 Die 的构造函数中,但要注意不要使用静态 bool 多次调用它。

Die::Die()
{
  static bool seeded = false;
  if (!seeded)
  {
    srand(time(NULL));
    seeded = true;
  }
}

请注意,这个 srand 仍然可以影响 srand() 和 rand() 的其他类,但很可能这无关紧要。但是,如果您在定义的多个类中需要一个随机数,您可以考虑创建一个 RNG 单例来为您生成数字。

【讨论】:

  • 一个更简单的解决方案是拥有一个静态变量,带有一个调用srand的初始化程序。
【解决方案3】:

最不容易出错的方法是使用 random 标头中的 uniform_int_distribution。这也避免了modulo bias,这是基本示例:

#include <iostream>
#include <random>

 class Die
 {
   public:
     template <class Generator>
     void roll( Generator &g )
     {
         std::uniform_int_distribution<int> dist(1,6);
         std::cout << dist(g) ;
     }

   private:

 } ;

int main()
{
    std::random_device rd;

    std::mt19937 e1(rd());

    Die d1, d2 ;

    for (int n = 0; n < 10; ++n) {
            d1.roll( e1 ) ;
            std::cout << " , " ;
            d2.roll( e1 ) ;
            std::cout << std::endl ;
    }
}

或者,您也可以使用 static 成员,正如 James 建议的那样:

class Die
{
   public:
     Die()  {} ;

     void roll()
     {
         std::uniform_int_distribution<int> dist(1,6);
         std::cout << dist(e1) ;
     }

   private:
      static std::random_device rd;    
      static std::mt19937 e1 ;
} ;

【讨论】:

  • 一个可以复杂的为什么要简单,对吧? (例如,为什么Die 的每个实例都应该有自己的生成器?为什么每次都将Generator 作为参数传递,而不是使用静态成员或单例?
  • @JamesKanze 我的意思是删除第二个生成器,传递生成器的选择是任意的,我添加了一个使用静态成员的示例。
【解决方案4】:

正如其他人所说,您不想再为生成器重新播种 不止一次。如果Die 是rand 的唯一用户(或者即使它 不是),您可以在名称空间范围内使用变量来播种:

int seeded = (srand( time( NULL ) ), rand());

这是否是一个好主意取决于;它成功了 无法复制您的代码运行以进行调试 目的,例如。如果这是一个问题,你会想要 1) 记录您使用的种子,以及 2) 在命令行中添加一个选项 指定种子;后者意味着调用srand from main。

(还要注意,我在 初始化。在至少一种广泛传播的实施中, 第一次调用rand() 返回种子,这意味着它可以 非常可预测。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-08-21
    • 2011-08-06
    • 2020-03-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-19
    • 1970-01-01
    相关资源
    最近更新 更多