¿Cuál de los motores de números aleatorios de <random> debería uno realmente usar en la práctica? std :: mt19937?

21

Suponga que desea utilizar las <random>instalaciones de C ++ en un programa práctico (para alguna definición de "práctico", las restricciones aquí son parte de esta pregunta). Tienes un código más o menos así:

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

Mi pregunta es, ¿para qué tipo debes usar ENGINE?

  • Solía ​​decir siempre std::mt19937porque era rápido escribir y tenía reconocimiento de nombre. Pero en estos días parece que todos dicen que el Mersenne Twister es muy pesado y poco amigable con el caché y ni siquiera pasa todas las pruebas estadísticas que otros hacen.

  • Me gustaría decirlo std::default_random_engineporque es el obvio "defecto". Pero no sé si varía de una plataforma a otra, y no sé si estadísticamente es bueno.

  • Dado que todos están en una plataforma de 64 bits en estos días, ¿al menos deberíamos usar std::mt19937_64más std::mt19937?

  • Me gustaría decir pcg64o xoroshiro128porque parecen respetados y ligeros, pero no existen en <random>absoluto.

  • No sé nada acerca de minstd_rand, minstd_rand0, ranlux24, knuth_b, etc - sin duda que debe ser bueno para algo?

Obviamente, hay algunas restricciones en competencia aquí.

  • Resistencia del motor. ( <random>no tiene PRNG criptográficamente fuertes, pero aún así, algunos de los estandarizados son "más débiles" que otros, ¿verdad?)

  • sizeof el motor.

  • Velocidad de su operator().

  • Facilidad de siembra. mt19937es notoriamente difícil de sembrar adecuadamente porque tiene mucho estado para inicializar.

  • Portabilidad entre vendedores de bibliotecas. Si un proveedor foo_engineproduce números diferentes de los de otro proveedor foo_engine, eso no es bueno para algunas aplicaciones. (Espero que esto no descarte nada excepto tal vez default_random_engine).

Sopesando todas estas restricciones lo mejor que pueda, ¿cuál diría que es la respuesta definitiva de "mejor práctica para mantenerse dentro de la biblioteca estándar"? ¿Debo seguir usando std::mt19937o qué?

Quuxplusone
fuente
2
Para su último punto, todos los adaptadores de motor estándar están especificados para devolver un valor particular en una invocación consecutiva particular del predeterminado construido, por lo que deberían ser portátiles.
1201ProgramAlarm

Respuestas:

15

C ++ Reference enumera todos los motores aleatorios actualmente proporcionados por C ++. Sin embargo, la selección de motores deja mucho que desear (por ejemplo, vea mi lista de generadores aleatorios de alta calidad ). Por ejemplo:

  • default_random_engine está definida por la implementación, por lo que se desconoce si el motor tiene fallas estadísticas que puedan interesarle a la aplicación.
  • linear_congruential_engineimplementa generadores lineales congruentes. Sin embargo, tienden a tener mala calidad a menos que el módulo sea primo y muy grande (al menos 64 bits). Además, no pueden admitir más semillas que su módulo.
  • minstd_rand0y minstd_randadmitir solo alrededor de 2 ^ 31 semillas. knuth_benvuelve a minstd_rand0y hace una mezcla de Bays-Durham.
  • mt19937y mt19937_64podría admitir muchas más semillas si se inicializaran mejor (por ejemplo, inicializando a std::seed_seqcon múltiples salidas de random_device, no solo una), pero usan aproximadamente 2500 bytes de estado.
  • ranlux24y ranlux48usan aproximadamente 577 bits de estado, pero son lentos (funcionan manteniendo algunos y descartando otras salidas pseudoaleatorias).

Sin embargo, C ++ también tiene dos motores que envuelven otro motor para mejorar potencialmente sus propiedades de aleatoriedad:

  • discard_block_engine descarta algunas de las salidas de un motor aleatorio dado.
  • shuffle_order_engine implementa una mezcla aleatoria de Bays-Durham de un motor aleatorio dado.

Por ejemplo, es posible, por ejemplo, tener un shuffle de Bays-Durham de mt19937, ranlux24o una costumbre linear_congruential_enginecon shuffle_order_engine. Quizás el motor envuelto sea de mejor calidad que el original. Sin embargo, es difícil predecir la calidad estadística del nuevo motor sin probarlo .

Por lo tanto, en espera de tales pruebas, parece que mt19937es el motor más práctico en el estándar C ++ por ahora. Sin embargo, soy consciente de al menos una propuesta para agregar otro motor de números aleatorios a futuras versiones de C ++ (consulte el documento C ++ P2075 ).

Peter O.
fuente
1

Según C ++ de referencia , default_random_engine:

Es la selección de la implementación de la biblioteca de un generador que proporciona al menos un comportamiento aceptable del motor para un uso relativamente informal, inexperto y / o liviano.

Entonces, para un uso liviano , no necesita preocuparse por nada, sembrar default_random_enginecon Epoch Time (time(0))y eso sería lo suficientemente bueno;)

Farbod Ahmadian
fuente
Creo que el problema aquí es la portabilidad. Si bien el valor predeterminado puede ser un motor que funciona bien, puede no ser reproducible en otra plataforma.
bremen_matt
@bremen_matt Hmm ... Bueno, ¿por qué necesitamos reproducir un número "aleatorio"?
Farbod Ahmadian
2
Pruebas. Para fines de prueba, necesita entradas reproducibles. Al mismo tiempo, es posible que desee o necesite que esas entradas sean aleatorias. Por ejemplo, la mayoría de los algoritmos de aprendizaje automático suponen que los parámetros se inicializan aleatoriamente. Ransac, CNN, DNN, ... muchos algoritmos requieren parámetros aleatorios.
bremen_matt