为什么在使用迭代器时必须重新设置随机生成器的种子?
Why do I have to reseed the random generator when using iterators?
我的目标是用随机值填充向量向量的每个向量的每个元素。请考虑以下代码:
#include <algorithm>
#include <iostream>
#include <random>
#include <vector>
typedef std::vector<int> IntVect;
typedef std::vector<IntVect> Grid;
void fillRandom(Grid& grid, int lo, int hi);
int main(int argc, const char * argv[]) {
Grid grid { Grid(5, IntVect (5)) };
fillRandom(grid, 0, 10);
for (auto& row : grid) {
for (auto& i : row) {
std::cout << i << " ";
}
std::cout << "\n";
}
return 0;
}
// fillRandom v1
void fillRandom(Grid& grid, int lo, int hi) {
// Init Random engine
std::random_device rnd_device;
std::mt19937 generator { rnd_device() };
std::uniform_int_distribution<int> distr(lo, hi);
auto dice = std::bind(distr, generator);
Grid::iterator i { grid.begin() };
for (; i != grid.end(); ++i) {
std::generate(i->begin(), i->end(), dice);
generator.seed(rnd_device()); // reseed
dice = std::bind(distr, generator);
}
return;
}
在 fillRandom
中,我必须为 grid
的每个元素重新播种生成器。否则 grid
的每个元素的输出都相同,例如:
7 5 8 1 9
7 5 8 1 9
7 5 8 1 9
7 5 8 1 9
7 5 8 1 9
。但是,如果我将 fillRandom
更改为:
// fillRandom v2
void fillRandom(Grid& grid, int lo, int hi) {
// Init Random engine as above
for (auto& row : grid)
for (auto& i : row)
i = dice();
return;
}
我没有为 grid
的每个 vector
重新播种生成器就得到了预期的结果。
在 fillRandom
的第二个版本中,为每个向量的每个元素调用 dice()
,导致 grid
填充随机值。然而,第一个版本应该完全一样。但显然不是。这里有什么区别?
为什么我在使用 std::generate
和迭代器时必须为每个向量重新设置随机生成器的种子?你能帮我理解这种行为吗?
行 std::generate(i->begin(), i->end(), dice);
按值获取 dice
,创建它的副本。这也会复制绑定的参数。这导致您每次都复制相同的生成器和分布,而不更改原始状态。每次迭代都从相同的状态开始。
在第二个示例中,您只需在循环中调用 dice
,就会导致生成器和分布的单个实例产生值,从而提升它们的内部状态,并导致不同的值在下一次迭代中。
改用 lamdba 试试。
// fillRandom v1
void fillRandom(Grid& grid, int lo, int hi) {
// Init Random engine
std::random_device rnd_device;
std::mt19937 generator{ rnd_device() };
std::uniform_int_distribution<int> distr(lo, hi);
auto dice = [&generator, &distr]() { return distr(generator); };
Grid::iterator i{ grid.begin() };
for (; i != grid.end(); ++i) {
std::generate(i->begin(), i->end(), dice);
}
return;
}
我的目标是用随机值填充向量向量的每个向量的每个元素。请考虑以下代码:
#include <algorithm>
#include <iostream>
#include <random>
#include <vector>
typedef std::vector<int> IntVect;
typedef std::vector<IntVect> Grid;
void fillRandom(Grid& grid, int lo, int hi);
int main(int argc, const char * argv[]) {
Grid grid { Grid(5, IntVect (5)) };
fillRandom(grid, 0, 10);
for (auto& row : grid) {
for (auto& i : row) {
std::cout << i << " ";
}
std::cout << "\n";
}
return 0;
}
// fillRandom v1
void fillRandom(Grid& grid, int lo, int hi) {
// Init Random engine
std::random_device rnd_device;
std::mt19937 generator { rnd_device() };
std::uniform_int_distribution<int> distr(lo, hi);
auto dice = std::bind(distr, generator);
Grid::iterator i { grid.begin() };
for (; i != grid.end(); ++i) {
std::generate(i->begin(), i->end(), dice);
generator.seed(rnd_device()); // reseed
dice = std::bind(distr, generator);
}
return;
}
在 fillRandom
中,我必须为 grid
的每个元素重新播种生成器。否则 grid
的每个元素的输出都相同,例如:
7 5 8 1 9
7 5 8 1 9
7 5 8 1 9
7 5 8 1 9
7 5 8 1 9
。但是,如果我将 fillRandom
更改为:
// fillRandom v2
void fillRandom(Grid& grid, int lo, int hi) {
// Init Random engine as above
for (auto& row : grid)
for (auto& i : row)
i = dice();
return;
}
我没有为 grid
的每个 vector
重新播种生成器就得到了预期的结果。
在 fillRandom
的第二个版本中,为每个向量的每个元素调用 dice()
,导致 grid
填充随机值。然而,第一个版本应该完全一样。但显然不是。这里有什么区别?
为什么我在使用 std::generate
和迭代器时必须为每个向量重新设置随机生成器的种子?你能帮我理解这种行为吗?
行 std::generate(i->begin(), i->end(), dice);
按值获取 dice
,创建它的副本。这也会复制绑定的参数。这导致您每次都复制相同的生成器和分布,而不更改原始状态。每次迭代都从相同的状态开始。
在第二个示例中,您只需在循环中调用 dice
,就会导致生成器和分布的单个实例产生值,从而提升它们的内部状态,并导致不同的值在下一次迭代中。
改用 lamdba 试试。
// fillRandom v1
void fillRandom(Grid& grid, int lo, int hi) {
// Init Random engine
std::random_device rnd_device;
std::mt19937 generator{ rnd_device() };
std::uniform_int_distribution<int> distr(lo, hi);
auto dice = [&generator, &distr]() { return distr(generator); };
Grid::iterator i{ grid.begin() };
for (; i != grid.end(); ++i) {
std::generate(i->begin(), i->end(), dice);
}
return;
}