伪随机数发生器给出相同的第一输出,但随后表现如预期

use*_*276 10 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;
}
Run Code Online (Sandbox Code Playgroud)

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

57
134
125
136
112
Run Code Online (Sandbox Code Playgroud)

在第二次尝试之前,我决定删除之间的空行uniform_int_distribution,int main()只是为了查看种子是否基于编译时间,如您所见,这无关紧要.

57
84
163
42
146
Run Code Online (Sandbox Code Playgroud)

再跑一次:

57
73
181
160
46
Run Code Online (Sandbox Code Playgroud)

所以在我的跑步中我一直是57第一个,这当然不是世界末日,如果我想要不同的输出我可以扔掉第一个输出.但是,这是否是设计(如果是这样,为什么?)或者我是否在某种程度上滥用发生器(如果是这样的话怎么样?)就会产生疑问.

Ric*_*ard 8

我不确定会出现什么问题(但是!),但你仍然可以按照以下方式初始化,而不会遇到问题(从这里借来).

#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;
}
Run Code Online (Sandbox Code Playgroud)

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

std::random_device rd;
default_random_engine gen(rd());
Run Code Online (Sandbox Code Playgroud)

运行

cout<<time(NULL)<<endl;
cout<<std::chrono::system_clock::now().time_since_epoch().count()<<endl;
cout<<rd()<<endl;
Run Code Online (Sandbox Code Playgroud)

在我的机器上生成

1413844318
1413844318131372773
3523898368
Run Code Online (Sandbox Code Playgroud)

所以该chrono库提供了比库更大的数字和更快速的数字(以纳秒为单位)ctime.的random_device是生产这些都是在地图上不确定性的数字.所以似乎种子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;
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

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

鉴于此,很明显为什么chronorandom_device库更好地工作:它们产生的种子总是超过669.请记住,即使第一个数字是相同的,在许多程序中重要的是由不同的数字生成的数字序列.