C++11 std::generate 和 std::uniform_real_distribution 调用两次会产生
C++11 std::generate and std::uniform_real_distribution called two times gives strange results
调用 std::generate 算法从 STL 在不同的容器上两次产生等效的结果。
考虑我想用 -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 将引用存储在绑定中而不是副本中:
auto rand = std::bind(dis, std::ref(gen));
使用返回引用的 lambda :
auto rand = [&](){ return dis(gen); };
我相信
这里发生的事情是 std::generate 按值获取其生成器参数。一旦你把它传进去,它就会被复制。这意味着 std::generate 内部发生的情况不会影响您传入的函数对象。
因此,您再次调用 generate 并复制相同的生成器函数对象,然后该对象开始以完全相同的状态生成,包括(内部某处)用于初始化数字生成的种子。
如果你创建了一个不同的生成器函数,即使使用相同的参数,你也必须在其中获得一个不同的随机种子。这就是您的其他方法产生不同结果的方式。最终,他们的生成器从不同的种子开始。
相关文章:
- g++的分段错误(在NaN上使用to_string两次时)
- 蛇在C++不会连续转两次
- 检查一个数组是否包含在另一个数组中,以相反的顺序,至少两次
- 从具有按值捕获的 lambda 移动构造 std::函数时,移动构造函数调用两次
- 我应该如何去缓解两次出现的cin?
- Realloc 两次无法在 Visual Studio 上运行
- 为什么参数在构造 std::thread 时移动两次
- 做 std::用相同的unique_ptr移动两次
- 对于优化级别为 0 的 std::vector,析构函数被调用两次
- std::getline 读取最后一个字符串两次
- STD :: Promise Set_exception两次导致分段故障
- 必须将 std::thread 加入 std::vector<std::thread> 两次以避免从线程 dtor 终止
- C++11 std::generate 和 std::uniform_real_distribution 调用两次会产生
- 为什么锁定 std::mutex 两次是"未定义的行为"?
- 两次读取std::cin的输入
- 插入 std::unordered_map 在 MSVC++ 的 STL 中调用哈希函数两次,设计不好还是特殊原因?
- std::make_shared在VS2012中进行了两次构造函数调用
- 为什么不能连续两次使用std::cin.getline() ?
- 自定义分配器- Microsoft std::map实现对相同的元素进行两次重新分配,GCC工作得很好
- std::condition_variable – 通知一次,但等待线程唤醒两次