为什么在使用迭代器时我必须重新设置随机生成器?

时间:2017-02-21 21:13:31

标签: c++ random stl-algorithm

我的目标是用随机值填充向量矢量的每个元素的每个元素。请考虑以下代码:

#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;
}

我得到了预期的结果,但没有为vector grid的每个fillRandom重新生成生成器。

dice() grid的第二个版本中,为每个向量的每个元素调用,导致std::generate填充随机值。但是,第一个版本应该完全相同。但它显然没有。这有什么区别?

当我使用21-FEB-17和迭代器时,为什么我必须为每个向量重新设置随机生成器?你能帮我理解一下这个行为吗?

1 个答案:

答案 0 :(得分:3)

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