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

2024-01-13

使用随机类和时间种子(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_distribution and int main()只是为了查看种子是否基于编译时间,如您所见,这并不重要。

57
84
163
42
146

刚刚再次运行:

57
73
181
160
46

所以在我的跑步中我不断得到57首先,这当然不是世界末日,如果我想要不同的输出,我可以扔掉第一个输出。但这带来了问题,这是否是设计使然(如果是这样为什么?)或者我是否以某种方式滥用了生成器(如果是怎样?)。


我不确定出了什么问题(还!),但您仍然可以按如下方式按时间初始化,而不会遇到问题(借自here http://www.cplusplus.com/reference/random/linear_congruential_engine/linear_congruential_engine/).

#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());

Running

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

在我的机器上生成

1413844318
1413844318131372773
3523898368

so the 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;
    }
  }
}

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

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)。

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

伪随机数生成器给出相同的第一个输出,但随后表现如预期 的相关文章

随机推荐