在不同容器上从STL调用std :: generate算法两次会产生相同的结果。
考虑我想要填充两个浮点数组,其中随机数介于-1之间。和1.:
std::array<float, 1000> x;
std::array<float, 1000> y;
std::random_device rd;
std::mt19937_64 gen(rd());
std::uniform_real_distribution<float> dis(-1.f, 1.f);
auto rand = std::bind(dis, gen);
std::generate(x.begin(), x.end(), rand);
std::generate(y.begin(), y.end(), rand);
您可以在此处测试:http://ideone.com/X712IU。两个数组都填充了完全相同的值:
0: -0.411968, -0.411968
1: 0.55158, 0.55158
2: 0.69889, 0.69889
3: -0.901328, -0.901328
4: -0.556142, -0.556142
5: -0.798431, -0.798431
6: -0.570874, -0.570874
7: 0.928999, 0.928999
8: 0.118056, 0.118056
9: -0.655123, -0.655123
现在,如果我在生成之间创建一个新的生成器,它可以正常工作:
std::array<float, 1000> x;
std::array<float, 1000> y;
// Generators in different scopes, OK
std::random_device rd;
{
std::mt19937_64 gen(rd());
std::uniform_real_distribution<float> dis(-1.f, 1.f);
auto rand = std::bind(dis, gen);
std::generate(x.begin(), x.end(), rand);
}
{
std::mt19937_64 gen(rd());
std::uniform_real_distribution<float> dis(-1.f, 1.f);
auto rand = std::bind(dis, gen);
std::generate(y.begin(), y.end(), rand);
}
给予:
0: 0.391496, -0.64993
1: 0.429592, 0.835015
2: 0.00735116, 0.77657
3: -0.548355, -0.0794801
4: -0.312095, -0.119841
5: 0.931296, 0.997449
6: -0.934924, -0.832223
7: 0.432267, 0.181224
8: 0.942709, 0.165024
9: 0.315852, -0.654576
现在使用for循环的同一个生成器,它也可以工作:
// Both arrays assigned in the same loop, OK
for(size_t i = 0; i < x.size(); ++i)
{
x[i] = rand();
y[i] = rand();
}
// Arrays in separated loops, OK
for(size_t i = 0; i < x.size(); ++i)
x[i] = rand();
for(size_t i = 0; i < y.size(); ++i)
y[i] = rand();
它看起来像是与std :: generate相关的东西,但我找不到会导致这种行为的原因。
任何解释?干杯
修改
正如dotcavader所指出的那样,问题只是因为生成器对象已被复制(由std :: bind和std :: generate)。因此,对于两个生成,生成器都以完全相同的内部状态启动。
为了完成答案,Praetorian和Casey为此提供了两个简单的解决方案:
使用std :: reference_wrapper在bind中存储引用而不是副本:
auto rand = std::bind(dis, std::ref(gen));
使用返回引用的lambda:
auto rand = [&](){ return dis(gen); };
答案 0 :(得分:6)
我相信这里发生的事情是std :: generate按值获取其生成器参数。一旦你传入它,它就被复制了。这意味着std :: generate中发生的事情不会影响您传入的函数对象。
所以你再次调用generate,然后你复制相同的生成器函数对象,然后开始生成完全相同的状态,包括(在那里的某个地方)哪个种子用于初始化数字生成。
如果创建不同的生成器函数,即使使用相同的参数,也必须在其中获取不同的随机种子。这就是您的其他方法产生不同结果的方式。最终,他们的发电机以不同的种子开始。