第一个随机数总是小于休息

时间:2015-05-25 01:29:12

标签: c++ qt random

我碰巧注意到在C ++中,使用std rand()方法调用的第一个随机数大部分时间都明显小于第二个。关于Qt实现,第一个几乎总是小几个。

qsrand(QTime::currentTime().msec());
qDebug() << "qt1: " << qrand();
qDebug() << "qt2: " << qrand();

srand((unsigned int) time(0));
std::cout << "std1: " << rand() << std::endl;
std::cout << "std2: " << rand() << std::endl;

输出:

qt1:  7109361
qt2:  1375429742
std1: 871649082
std2: 1820164987

这是因为播种错误还是错误? 此外,当qrand()输出强烈变化时,第一个rand()输出似乎随时间线性变化。只是想知道为什么。

3 个答案:

答案 0 :(得分:15)

我不确定是否可归类为错误,但它有一个解释。让我们来看看情况:

  1. 请看rand's implementation。您将看到它只是使用上一个生成的值进行计算。

  2. 您正在使用 QTime :: currentTime()。msec()播种,其本质上是由小范围的值 0..999 <限定的/ em>,但 qsrand 接受 0..4294967295 范围内的 uint 变量。

  3. 通过结合这两个因素,你就有了一种模式。

    出于好奇:尝试播种 QTime :: currentTime()。msec()+ 100000000

    现在,第一个值可能会大于第二个值。

    我不会太担心。这种&#34;模式&#34;似乎只发生在前两个生成的值上。在那之后,一切似乎都恢复正常。

    修改

    为了更清楚,请尝试运行以下代码。它将前两个生成的值进行比较,以查看哪一个较小,使用所有可能的毫秒值(范围:0..999)作为种子:

    int totalCalls, leftIsSmaller = 0;
    for (totalCalls = 0; totalCalls < 1000; totalCalls++)
    {
        qsrand(totalCalls);
        if (qrand() < qrand())
            leftIsSmaller++;
    }
    qDebug() << (100.0 * leftIsSmaller) / totalCalls;
    

    它将打印94.8,这意味着94.8%的时间第一个值将小于第二个值。

    结论:当使用当前毫秒播种时,您将看到前两个值的模式。我在这里做了一些测试,在生成第二个值后,模式似乎消失了。我的建议是:找到一个好的&#34;调用 qsrand 的值(显然应该只在程序开头调用一次)。一个好的值应该跨越 uint 类的整个范围。看看这个问题的另一个问题:

    另外,看看这个:

答案 1 :(得分:12)

目前的Qt和C标准运行时都没有质量随机发生器,您的测试显示。 Qt似乎使用C运行时(这很容易检查,但为什么)。如果您的项目中有C ++ 11可用,那么使用更好更方便的方法:

#include <random>
#include <chrono>

auto seed = std::chrono::system_clock::now().time_since_epoch().count();
std::default_random_engine generator(seed);
std::uniform_int_distribution<uint> distribution;
uint randomUint = distribution(generator);

有很好的video涵盖了这个主题。正如评论者user2357112所述,我们可以应用不同的随机引擎,然后应用不同的发行版,但对于我的具体用途,上面的工作非常好。

答案 2 :(得分:8)

请记住,基于少量样本对统计现象做出判断可能会产生误导,我决定进行一项小型实验。我运行以下代码:

int main()
{
  int i = 0;
  int j = 0;
  while (i < RAND_MAX)
  {
    srand(time(NULL));
    int r1 = rand();
    int r2 = rand();
    if (r1 < r2) 
      ++j;
    ++i;
    if (i%10000 == 0) {
      printf("%g\n", (float)j / (float)i);
    }
  }
}

基本上打印了第一个生成的数字小于第二个的百分比。您可以在下面看到该比率的图表:

enter image description here

并且正如您所看到的,在实际新种子少于50之后它实际上接近0.5。

正如评论中所建议的,我们可以修改代码以在每次迭代时使用连续种子并加速收敛:

int main()
{
  int i = 0;
  int j = 0;
  int t = time(NULL);
  while (i < RAND_MAX)
  {
    srand(t);
    int r1 = rand();
    int r2 = rand();
    if (r1 < r2)
      ++j;
    ++i;
    if (i%10000 == 0) {
      printf("%g\n", (float)j / (float)i);
    }
    ++t;
  }
}

这给了我们:

enter image description here

也非常接近0.5。

虽然rand肯定不是最好的伪随机数生成器,但声称它在第一次运行期间经常生成较小的数字似乎并不合理