Qual dos mecanismos de números aleatórios de <random> deve-se realmente usar na prática? std :: mt19937?

21

Suponha que você queira usar os recursos do C ++ <random>em um programa prático (para alguma definição de "prático" - as restrições aqui fazem parte dessa pergunta). Você tem código mais ou menos assim:

int main(int argc, char **argv) {
    int seed = get_user_provided_seed_value(argc, argv);
    if (seed == 0) seed = std::random_device()();
    ENGINE g(seed);  // TODO: proper seeding?
    go_on_and_use(g);
}

Minha pergunta é: para que tipo você deve usar ENGINE?

  • Eu costumava dizer sempre std::mt19937porque era rápido para digitar e tinha reconhecimento de nome. Mas hoje em dia parece que todo mundo está dizendo que o Mersenne Twister é muito pesado e hostil ao cache e nem passa em todos os testes estatísticos que os outros fazem.

  • Eu gostaria de dizer std::default_random_engineporque é o óbvio "padrão". Mas não sei se isso varia de plataforma para plataforma e não sei se é estatisticamente bom.

  • Como hoje em dia todo mundo está em uma plataforma de 64 bits, devemos pelo menos estar usando std::mt19937_64demais std::mt19937?

  • Eu gostaria de dizer pcg64ou xoroshiro128porque eles parecem bem respeitados e leves, mas não existem <random>.

  • Eu não sei nada sobre minstd_rand, minstd_rand0, ranlux24, knuth_b, etc. - certamente eles devem ser bom para alguma coisa?

Obviamente, existem algumas restrições concorrentes aqui.

  • Força do motor. ( <random>não possui PRNGs criptograficamente fortes, mas ainda assim, alguns dos padronizados são "mais fracos" do que outros, certo?)

  • sizeof o motor.

  • Velocidade do seu operator().

  • Facilidade de semear. mt19937é notoriamente difícil de propagar adequadamente porque tem muito estado para inicializar.

  • Portabilidade entre fornecedores de bibliotecas. Se um fornecedor foo_engineproduz números diferentes dos de outro fornecedor foo_engine, isso não é bom para alguns aplicativos. (Espero que isso não descarte nada, exceto talvez default_random_engine.)

Pesando todas essas restrições da melhor maneira possível, o que você diria que é a melhor resposta para "melhor prática de permanecer dentro da biblioteca padrão"? Devo continuar usando std::mt19937ou o quê?

Quuxplusone
fonte
2
Até o último ponto, todos os adaptadores de mecanismo padrão são especificados para retornar um valor específico em uma chamada consecutiva específica do padrão construído, portanto, eles devem ser portáteis.
1201ProgramAlarm

Respostas:

15

A referência C ++ lista todos os mecanismos aleatórios atualmente fornecidos pelo C ++. No entanto, a seleção de motores deixa muito a desejar (por exemplo, veja minha lista de geradores aleatórios de alta qualidade ). Por exemplo:

  • default_random_engine é definido pela implementação, portanto, não se sabe se o mecanismo possui falhas estatísticas com as quais o aplicativo pode se importar.
  • linear_congruential_engineimplementa geradores congruenciais lineares. No entanto, eles tendem a ter baixa qualidade, a menos que o módulo seja primário e muito grande (pelo menos 64 bits). Além disso, eles não podem admitir mais sementes do que seu módulo.
  • minstd_rand0e minstd_randadmita apenas cerca de 2 ^ 31 sementes. knuth_benvolve um minstd_rand0e faz um barulho de Bays-Durham.
  • mt19937e mt19937_64poderia admitir muito mais sementes se fossem melhor inicializadas (por exemplo, inicializando a std::seed_seqcom várias saídas random_device, não apenas uma), mas elas usam cerca de 2500 bytes de estado.
  • ranlux24e ranlux48usam cerca de 577 bits de estado, mas são lentos (eles funcionam mantendo alguns e descartando outras saídas pseudo-aleatórias).

No entanto, o C ++ também possui dois mecanismos que envolvem outro mecanismo para melhorar potencialmente suas propriedades de aleatoriedade:

  • discard_block_engine descarta algumas das saídas de um determinado mecanismo aleatório.
  • shuffle_order_engine implementa um shuffle de Bays-Durham de um determinado mecanismo aleatório.

Por exemplo, é possível, por exemplo, ter um Shuffle Bays-Durham de mt19937, ranlux24ou um costume linear_congruential_enginecom shuffle_order_engine. Talvez o motor envolvido seja de melhor qualidade que o original. No entanto, é difícil prever a qualidade estatística do novo mecanismo sem testá-lo .

Portanto, enquanto se aguarda esses testes, parece que mt19937é o mecanismo mais prático no padrão C ++ por enquanto. Estou ciente, no entanto, de pelo menos uma proposta para adicionar outro mecanismo de números aleatórios a versões futuras do C ++ (consulte o documento C ++ P2075 em C ++ ).

Peter O.
fonte
1

De acordo com a ++ de Referência C , default_random_engine:

É a seleção da implementação da biblioteca de um gerador que fornece pelo menos um comportamento aceitável do mecanismo para uso relativamente casual, inexperiente e / ou leve.

Assim, para uso leve você não precisa ser preocupar com nada, semente default_random_enginecom Epoch Time (time(0))e que seria bom o suficiente;)

Farbod Ahmadian
fonte
Eu acredito que o problema aqui é portabilidade. Embora o padrão possa ser um mecanismo com bom desempenho, ele pode não ser reproduzível em outra plataforma.
bremen_matt
@bremen_matt Hmm ... Bem, por que precisamos reproduzir um número "aleatório"?
Farbod Ahmadian
2
Teste. Para fins de teste, você precisa de entradas reproduzíveis. Ao mesmo tempo, você pode querer ou precisar que essas entradas sejam aleatórias. Por exemplo, a maioria dos algoritmos de aprendizado de máquina pressupõe que os parâmetros sejam inicializados aleatoriamente. Ransac, CNNs, DNNs, ... muitos algoritmos requerem parâmetros aleatórios.
bremen_matt