伪随机数生成器给出相同的第一个输出,但随后的行为和预期的一样
Pseudo random number generator gives same first output but then behaves as expected
使用随机类和时间种子(NULL),均匀分布总是给出相同的第一个输出,即使有不同的compiling,但在第一个输出之后,行为与您期望的伪随机数生成器的行为相同。
这是由于结构原因,还是我使用错误?
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
和int main()
之间的空行,只是为了看看种子是否基于编译时,正如您所看到的,这无关紧要。
57
84
163
42
146
只是再次运行:
57
73
181
160
46
所以在我的跑步中,我总是先得到57
,这当然不是世界末日,如果我想要不同的输出,我可以放弃第一个输出。但这让人怀疑这是故意的(如果是为什么?)还是我以某种方式滥用了生成器(如果是如何使用的?)。
我还不确定出了什么问题(还!),但您仍然可以按如下时间进行初始化,而不会遇到问题(从这里借用)。
#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
因此CCD_ 4库提供了比CCD_。random_device
正在产生遍布地图的非确定性数字。因此,似乎ctime
产生的种子可能在某种程度上过于紧密,从而部分映射到相同的内部状态?
我做了另一个程序,看起来像这样:
#include <iostream>
#include <random>
using namespace std;
int main(){
int oldval = -1;
unsigned int oldseed = -1;
cout<<"SeedtValuetSeed 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
和random_device
库工作得更好的原因就很清楚了:它们生成的种子总是相距669以上。请记住,即使第一个数字是相同的,在许多程序中重要的是由不同的数字生成的序列。
使用std::default_random_engine就像在一家糟糕的餐厅里说"给我惊喜!"。你唯一可以确定的是,结果会很差——因为<random>
提供的生成器都有缺陷——但你甚至不知道你必须处理哪些特定的缺陷。
Mersenne Twister是一个不错的选择,如果——而且只有当——它被正确地播种,并且存在摩擦。理想情况下,种子的每一个比特都应该以相等的概率影响结果生成器状态的每一比特;正如您所发现的,std::mersennetwister_engine的常见实现中并非如此。
Mersenne Twister通常用一个更简单的PRNG的输出进行初始化,而PRNG又被任何可用的熵播种。这有效地将更简单的PRNG的种子熵扩展到龙卷风的巨大状态上。该标准的制定者为此深思熟虑地提供了seed_seq接口;然而,该库似乎不包含任何适配器,用于将生成器用作种子序列。
两种不同的播种概念之间也存在差异。在生成器方面,种子函数应该获取传递的熵,并将其忠实地映射到生成器状态,确保在这个过程中不会丢失熵。在用户端,它是"取这些数字,给我不同的序列",其中"这些数字"是{1,2,3,…}或clock()输出。
换句话说,种子熵以不适合直接初始化生成器状态的形式提供;小的种子差异产生小的状态差异。这对于像Mersenne Twister或为std::ranluxXX生成器供电的滞后Fibonacci这样的巨大滞后生成器来说尤其有问题。
比特混合函数是一种双射函数,其中输出的每个比特都以相等的概率依赖于输入的每个比特。比特混合函数可以帮助使1、2、3或clock()输出等种子对种子更有用。杂音散列混合器通过实现几乎完美的扩散(显示的是32位版本)接近这一理想:
uint32_t murmur_mix32 (uint32_t x)
{
x ^= x >> 16;
x *= 0x85EBCA6B;
x ^= x >> 13;
x *= 0xC2B2AE35;
x ^= x >> 16;
return x;
}
该函数是双射的,因此它根本不会失去任何熵。这意味着你可以用它来改善任何种子,而不会有让事情变得更糟的危险。
另一个快速解决方案是在生成器上调用discard(),该参数取决于(杂音混合)种子,而无需制作seed_seq。然而,对像Mersenne Twister这样的大型生成器的影响是有限的,因为它们的状态发展非常缓慢,需要数十万次迭代才能从缺陷状态中完全恢复。
您正在使用的种子可能会引入偏差,如果使用不同的种子产生相同的结果,则生成器本身没有正确写入。
我建议用不同的种子进行测试以得出结论。
- 为什么随机数生成器不在void函数中随机化数字,而在main函数中随机化
- 制作具有平均值的随机数生成器
- C++ 随机数生成器:尝试将结果作为向量获取,但通过制作 void 函数来执行此操作而出现错误
- 尝试在 c++ 中创建随机数生成器并收到错误
- 存储并重新加载随机数生成器状态
- 作为随机数生成器的争用条件
- 播种随机数生成器以使用分叉的好方法是什么?
- 随机数生成器的性能因平台而异
- 随机数生成器的种子和状态有什么区别?
- RcppShark 随机森林示例引发有关随机数生成器的异常
- C++多个实例的随机数生成器组种子中增强
- C++随机数生成器仅生成 0 - C++ 11
- GSL+OMP:C++中的线程安全随机数生成器
- 贪吃蛇游戏 - 食物瓷砖的随机数生成器
- 随机数生成器在 C++ 用于 skipList.
- RSA 加密和解密期间的随机数生成器要求
- 如何使用 cpp 设置随机数生成器的语法?
- 产生相同结果的随机数生成器
- 伪随机数生成器给出相同的第一个输出,但随后的行为和预期的一样
- 我可以像在 R 中一样在 R 中拥有多个独立的随机数生成器吗C++