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

The*_*hin 40 c++ random qt

我碰巧注意到在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;
Run Code Online (Sandbox Code Playgroud)

输出:

qt1:  7109361
qt2:  1375429742
std1: 871649082
std2: 1820164987
Run Code Online (Sandbox Code Playgroud)

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

Raf*_*iro 15

我不确定这可能被归类为错误,但它有一个解释.我们来看看情况:

  1. 看看兰德的实现.您将看到它只是使用上一个生成的值进行计算.

  2. 您正在使用QTime :: currentTime().msec()播种,它本质上由小范围的值0..999限制,但qsrand接受一个uint变量,范围为0..4294967295.

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

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

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

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

编辑:

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

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

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

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

另外,看看这个:

  • 种子的小范围是重要的一点,可能比一开始的确切模式更重要.即使没有可见的模式,也有很大的风险,即程序的两次运行将使用完全相同的种子. (5认同)

Ale*_*r V 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);
Run Code Online (Sandbox Code Playgroud)

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

  • 你可以在答案中包含视频的原因,而不仅仅是链接视频吗? (11认同)
  • 不,我的意思是包括视频给出的实际原因,而不仅仅是将链接放在答案中.除了使用不限于0-999范围的种子之外,我不明白为什么这个片段应该更好.我不认为视频实际上支持你声称这更好; 该视频似乎支持使用特定的生成器,如`std :: mt19937`,而且*特别不鼓励*使用`std :: default_random_engine`.见29:20. (8认同)
  • 链接到这样的视频通常没那么有用,因为当未来的访问者发现这篇文章时,链接可能不会被激活.我建议概述视频中提出的要点并引用它作为参考,而不仅仅是链接. (6认同)
  • 你能解释是什么让这更好或更可靠吗? (3认同)

And*_*bis 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);
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

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

在此输入图像描述

正如你所看到的,实际新种子实际上不到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;
  }
}
Run Code Online (Sandbox Code Playgroud)

这给了我们:

在此输入图像描述

它也非常接近0.5.

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

  • @jwg我同意这一点.值得注意的是,种子不是均匀随机选择的.这个答案和问题都对'srand`的输入产生了偏差,但它们并没有以同样的方式扭曲. (3认同)
  • 最好在循环中计算`time(NULL)`,并在循环中手动递增种子. (2认同)
  • 该图表似乎表明,虽然第一个数字不低于第二个数字的一​​半以上,但是每次运行的时间都非常长,远远超过硬币翻转期望的时间. (2认同)