C ++ 11 std :: generate e std :: uniform_real_distribution chamados duas vezes dão resultados estranhos
Chamar o algoritmo std :: generate do STL duas vezes em contêineres diferentes produz resultados equivalentes.
Considere que eu quero preencher dois arrays float com números aleatórios entre -1. e 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);
Você pode testá-lo aqui:http://ideone.com/X712IU. Ambas as matrizes são preenchidas com os mesmos valores exatos:
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
Agora, se eu fizer um novo gerador entre os gera funciona ok:
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);
}
Dá:
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
E agora com o mesmo gerador usando loops, ele também funciona:
// 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();
Parece algo relacionado a std :: generate, mas não consigo descobrir o que causaria esse comportamento.
Alguma explicação? Felicidades
EDITAR:
Como apontado por dotcavader, o problema vem simplesmente porque o objeto gerador foi copiado (ambos por std :: bind e std :: generate). Portanto, o gerador começa com o mesmo estado interno exato para ambos os geradores.
Só para completar a resposta, Praetorian e Casey deram duas soluções fáceis para isso:
Usando std :: reference_wrapper para armazenar uma referência na ligação em vez de uma cópia:
auto rand = std::bind(dis, std::ref(gen));
Usando um lambda que retorna uma referência:
auto rand = [&](){ return dis(gen); };