O std :: mt19937 requer aquecimento?

Eu li que muitos geradores de números pseudo-randoms requerem muitas amostras ordenadas para serem “aquecidas”. É esse o caso ao usar std :: random_device para seed std :: mt19937, ou podemos esperar que esteja pronto após a construção? O código em questão:

#include  std::random_device rd; std::mt19937 gen(rd()); 

Mersenne Twister é um pRNG baseado em registro de turnos (gerador de números pseudo-randoms) e, portanto, está sujeito a sementes ruins com longos ciclos de 0s ou 1s que levam a resultados relativamente previsíveis até que o estado interno esteja misturado o suficiente.

No entanto, o construtor que usa um valor único usa uma function complicada nesse valor de semente, que é projetada para minimizar a probabilidade de produzir tais estados “ruins”. Há uma segunda maneira de inicializar mt19937 onde você define diretamente o estado interno, através de um object em conformidade com o conceito SeedSequence. É esse segundo método de boot onde você pode precisar se preocupar em escolher um estado “bom” ou fazer um aquecimento.


O padrão inclui um object em conformidade com o conceito SeedSequence, chamado seed_seq . seed_seq obtém um número arbitrário de valores seed_seq de input e, em seguida, executa determinadas operações nesses valores para produzir uma seqüência de valores diferentes adequados para definir diretamente o estado interno de um pRNG.

Aqui está um exemplo de carregamento de uma seqüência de sementes com dados randoms suficientes para preencher todo o estado std::mt19937 :

 std::array seed_data; std::random_device r; std::generate_n(seed_data.data(), seed_data.size(), std::ref(r)); std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); std::mt19937 eng(seq); 

Isso garante que todo o estado seja random. Além disso, cada mecanismo especifica quantos dados ele lê a partir da seed_sequence, portanto, você pode querer ler os documentos para encontrar essas informações para qualquer mecanismo que você usa.

Embora aqui eu carregue o seed_seq inteiramente de std::random_device , seed_seq é especificado de tal forma que apenas alguns números que não são particularmente randoms devem funcionar bem. Por exemplo:

 std::seed_seq seq{1, 2, 3, 4, 5}; std::mt19937 eng(seq); 

Nos comentários abaixo, Cubbi indica que seed_seq funciona executando uma sequência de aquecimento para você.

Aqui está o que deve ser seu ‘padrão’ para propagação:

 std::random_device r; std::seed_seq seed{r(), r(), r(), r(), r(), r(), r(), r()}; std::mt19937 rng(seed); 

Eu acredito que há situações em que o MT pode ser semeado “mal”, o que resulta em seqüências não ótimas. Se bem me lembro, semear com todos os zeros é um desses casos. Eu recomendo que você tente usar os geradores WELL se este for um problema sério para você. Eu acredito que eles são mais flexíveis – a qualidade da semente não importa tanto. (Talvez para responder à sua pergunta mais diretamente: é provavelmente mais eficiente concentrar-se em semear bem ao invés de semear mal, em seguida, tentar gerar um monte de amostras para obter o gerador para um estado ideal.)

Se você semear com apenas um valor de 32 bits, tudo o que você conseguirá é uma das mesmas trajetórias de 2 ^ 32 através do espaço de estados. Se você usar um PRNG com KiBs de estado, provavelmente deverá semear tudo isso. Como descrito nos comentários da resposta do @ bames63 ‘, usar std::seed_seq provavelmente não é uma boa idéia se você quiser iniciar o estado inteiro com números randoms. Infelizmente, std::random_device não está em conformidade com o conceito SeedSequence , mas você pode escrever um wrapper que:

 #include  #include  #include  #include  class random_device_wrapper { std::random_device *m_dev; public: using result_type = std::random_device::result_type; explicit random_device_wrapper(std::random_device &dev) : m_dev(&dev) {} template  void generate(RandomAccessIterator first, RandomAccessIterator last) { std::generate(first, last, std::ref(*m_dev)); } }; int main() { auto rd = std::random_device{}; auto seedseq = random_device_wrapper{rd}; auto mt = std::mt19937{seedseq}; for (auto i = 100; i; --i) std::cout << mt() << std::endl; } 

Isso funciona pelo menos até você ativar conceitos. Dependendo se o seu compilador sabe sobre o SeedSequence como um concept C ++ 20, ele pode não funcionar porque estamos fornecendo apenas o método generate() ausente, nada mais. Na programação de modelos de tipo duck, esse código é suficiente, porque o PRNG não armazena o object de seqüência de sementes.

Intereting Posts