Lequel des moteurs de nombres aléatoires de <random> devrait-on réellement utiliser dans la pratique? std :: mt19937?

21

Supposons que vous souhaitiez utiliser les fonctionnalités C ++ <random>dans un programme pratique (pour une définition de "pratique" - les contraintes font en quelque sorte partie de cette question). Vous avez du code à peu près comme ceci:

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

Ma question est, quel type devez-vous utiliser ENGINE?

  • J'avais l'habitude de dire toujours std::mt19937parce qu'il était rapide à taper et avait une reconnaissance de nom. Mais ces jours-ci, il semble que tout le monde dit que le Mersenne Twister est très lourd et sans cache et ne passe même pas tous les tests statistiques que d'autres font.

  • Je voudrais dire std::default_random_engineparce que c'est le "défaut" évident. Mais je ne sais pas si cela varie d'une plateforme à l'autre, et je ne sais pas si c'est statistiquement bon.

  • Puisque tout le monde est sur une plate-forme 64 bits de nos jours, devrions-nous au moins utiliser std::mt19937_64Over std::mt19937?

  • Je voudrais dire pcg64ou xoroshiro128parce qu'ils semblent bien respectés et légers, mais ils n'existent pas <random>du tout.

  • Je ne sais rien minstd_rand, minstd_rand0, ranlux24, knuth_b, etc. - Ils doivent sûrement être bon pour quelque chose?

De toute évidence, il existe ici des contraintes concurrentes.

  • Force du moteur. ( <random>Il n'y a pas de PRNG cryptographiquement solides, mais quand même, certains standardisés sont "plus faibles" que d'autres, non?)

  • sizeof le moteur.

  • Vitesse de son operator().

  • Facilité d'ensemencement. mt19937est notoirement difficile à semer correctement car il a tellement d'état à initialiser.

  • Portabilité entre les fournisseurs de bibliothèques. Si un fournisseur foo_engineproduit des numéros différents d'un autre fournisseur foo_engine, ce n'est pas bon pour certaines applications. (J'espère que cela n'exclut rien sauf peut-être default_random_engine.)

En pesant toutes ces contraintes du mieux que vous le pouvez, quelle serait, selon vous, la réponse ultime "meilleures pratiques restant dans la bibliothèque standard"? Dois-je continuer à utiliser std::mt19937, ou quoi?

Quuxplusone
la source
2
Pour votre dernier point, tous les adaptateurs de moteur standard sont spécifiés pour renvoyer une valeur particulière lors d'une invocation consécutive particulière de celle construite par défaut, ils doivent donc être portables.
1201ProgramAlarm

Réponses:

15

La référence C ++ répertorie tous les moteurs aléatoires actuellement fournis par C ++. Cependant, la sélection des moteurs laisse beaucoup à désirer (par exemple, voir ma liste de générateurs aléatoires de haute qualité ). Par exemple:

  • default_random_engine est défini par l'implémentation, donc on ne sait pas si le moteur présente des failles statistiques dont l'application peut se soucier.
  • linear_congruential_engineimplémente des générateurs congruentiels linéaires. Cependant, ils ont tendance à être de mauvaise qualité à moins que le module ne soit premier et très grand (au moins 64 bits). De plus, ils ne peuvent pas admettre plus de graines que leur module.
  • minstd_rand0et minstd_randadmettre seulement environ 2 ^ 31 graines. knuth_benveloppe un minstd_rand0et fait un mélange Bays – Durham de celui-ci.
  • mt19937et mt19937_64pourrait admettre beaucoup plus de graines si elles étaient mieux initialisées (par exemple, en initialisant un std::seed_seqavec plusieurs sorties de random_device, pas une seule), mais elles utilisent environ 2500 octets d'état.
  • ranlux24et ranlux48utilisent environ 577 bits d'état mais ils sont lents (ils fonctionnent en gardant certains et en rejetant d'autres sorties pseudo-aléatoires).

Cependant, C ++ possède également deux moteurs qui enveloppent un autre moteur pour potentiellement améliorer ses propriétés de caractère aléatoire:

  • discard_block_engine supprime certaines des sorties d'un moteur aléatoire donné.
  • shuffle_order_engine implémente un mélange Bays – Durham d'un moteur aléatoire donné.

Par exemple, il est possible, par exemple, d'avoir une lecture aléatoire Bays-Durham mt19937, ranlux24ou une coutume linear_congruential_engineavec shuffle_order_engine. Peut-être que le moteur enveloppé est de meilleure qualité que l'original. Cependant, il est difficile de prédire la qualité statistique du nouveau moteur sans le tester .

Ainsi, en attendant de tels tests, il semble que ce mt19937soit le moteur le plus pratique du standard C ++ pour l'instant. Je connais cependant au moins une proposition visant à ajouter un autre moteur de nombres aléatoires aux futures versions de C ++ (voir le document C ++ P2075 ).

Peter O.
la source
1

Selon C ++ Reference , default_random_engine:

Est la sélection par l'implémentation de la bibliothèque d'un générateur qui fournit au moins un comportement de moteur acceptable pour une utilisation relativement décontractée, inexperte et / ou légère.

Donc, pour une utilisation légère, vous n'avez pas à vous soucier de quoi que ce soit, des graines default_random_engineavec Epoch Time (time(0))et ce serait assez bien;)

Farbod Ahmadian
la source
Je crois que le problème ici est la portabilité. Bien que la valeur par défaut soit un moteur qui fonctionne bien, il peut ne pas être reproductible sur une autre plate-forme.
bremen_matt
@bremen_matt Hmm ... Eh bien, pourquoi devons-nous reproduire un nombre "aléatoire"?
Farbod Ahmadian
2
Essai. À des fins de test, vous avez besoin d'entrées reproductibles. En même temps, vous pouvez souhaiter ou avoir besoin que ces entrées soient aléatoires. Par exemple, la plupart des algorithmes d'apprentissage automatique supposent que les paramètres sont initialisés de manière aléatoire. Ransac, CNN, DNN, ... de nombreux algorithmes nécessitent des paramètres aléatoires.
bremen_matt