c++ - 伪随机数生成器给出相同的第一个输出,但随后按预期运行

标签 c++ random

使用随机类和时间种子 (NULL),均匀分布始终给出相同的第一个输出,即使使用不同的编译,但在第一个输出之后的行为与您期望的伪随机数生成器的行为相同。

这是构造出来的,还是我使用不当?

MWE:

#include <ctime>
#include <iostream>
#include <random>

using namespace std;

default_random_engine gen(time(NULL));
uniform_int_distribution<int> dist(10,200);

int main()
{
    for(int i = 0; i < 5; i++)
        cout<<dist(gen)<<endl;

    return 0;
}

我运行这个程序的前三次得到的输出是:

57
134
125
136
112

在第二次尝试之前,我决定删除 uniform_int_distributionint main() 之间的空行,看看种子是否基于编译时间,如您所见,那没关系。

57
84
163
42
146

再次运行:

57
73
181
160
46

所以在我的运行中,我总是先得到 57,这当然不是世界末日,如果我想要不同的输出,我可以丢弃第一个输出。但这是否是设计使然(如果是,为什么?)还是我是否以某种方式滥用了生成器(如果是,如何?)。

最佳答案

我不确定出了什么问题(现在!),但您仍然可以按如下方式按时间初始化而不会遇到问题(借自 here)。

#include <ctime>
#include <iostream>
#include <random>
#include <chrono>

using namespace std;

unsigned seed1 = std::chrono::system_clock::now().time_since_epoch().count();

default_random_engine gen(seed1); //gen(time(NULL));
uniform_int_distribution<int> dist(10,200);

int main()
{
    for(int i = 0; i < 5; i++)
        cout<<dist(gen)<<endl;

    return 0;
}

您还可以使用不确定的随机设备(它从您的击键、鼠标移动和其他来源窃取时间信息以生成不可预测的数字)。这是您可以选择的最强种子,但如果您不需要强有力的保证,计算机时钟是更好的选择,因为如果您经常使用它,计算机可能会耗尽“随机性”(需要多次击键和鼠标运动来产生一个真正的随机数)。

std::random_device rd;
default_random_engine gen(rd());

运行

cout<<time(NULL)<<endl;
cout<<std::chrono::system_clock::now().time_since_epoch().count()<<endl;
cout<<rd()<<endl;

在我的机器上生成

1413844318
1413844318131372773
3523898368

因此 chrono 库提供了比 ctime 库大得多且变化更快的数字(以纳秒为单位)。 random_device 正在生成遍布 map 的不确定数字。所以似乎 ctime 正在生成的种子可能以某种方式靠得太近,因此部分映射到相同的内部状态?

我做了另一个程序,看起来像这样:

#include <iostream>
#include <random>
using namespace std;

int main(){
  int oldval           = -1;
  unsigned int oldseed = -1;

  cout<<"Seed\tValue\tSeed Difference"<<endl;
  for(unsigned int seed=0;seed<time(NULL);seed++){
    default_random_engine gen(seed);
    uniform_int_distribution<int> dist(10,200);
    int val = dist(gen);
    if(val!=oldval){
      cout<<seed<<"\t"<<val<<"\t"<<(seed-oldseed)<<endl;
      oldval  = val;
      oldseed = seed;
    }
  }
}

如您所见,这只是打印出当前时间之前每个可能的随机种子的第一个输出值,以及具有相同值的种子和先前种子的数量。输出的摘录如下所示:

Seed  Value Seed Difference
0 10  1
669 11  669
1338  12  669
2007  13  669
2676  14  669
3345  15  669
4014  16  669
4683  17  669
5352  18  669
6021  19  669
6690  20  669
7359  21  669
8028  22  669
8697  23  669
9366  24  669
10035 25  669
10704 26  669
11373 27  669
12042 28  669
12711 29  669
13380 30  669
14049 31  669

因此对于每个新的第一个数字,有 669 个种子给出了第一个数字。因为第二个和第三个数字不同,我们仍然在生成唯一的内部状态。我认为我们必须更多地了解 default_random_engine 才能了解 669(可以分解为 3 和 223)的特殊之处。

鉴于此,很清楚为什么 chronorandom_device 库工作得更好:它们生成的种子总是相差 669 以上。请记住,即使第一个数字相同,在许多程序中重要的是不同生成的数字序列。

关于c++ - 伪随机数生成器给出相同的第一个输出,但随后按预期运行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26475595/

相关文章:

c++ - 使用动态链接从 .dll 库调用函数

mysql - ORDER BY RAND() 替代方案

python - 如何编译具有可变输入类型的 numba jit'ed 函数?

algorithm - 是否可以使用对伪随机选择的 IP 地址的 ping 生成真正的随机数?

c++ - 一个 C++ 迭代器适配器,它包装和隐藏内部迭代器并转换迭代类型

c++ - QPropertyAnimation 不工作

c++ - {} 在这些 C++ 函数中意味着什么?

c++ - 由多个线程访问的队列的推荐模式......工作线程应该做什么?

matlab - 如何在 MATLAB 中生成二维随机向量?

java - 每天更新随机字符串