Générer des nombres aléatoires uniformément sur une plage entière

J’ai besoin de générer des nombres aléatoires dans un intervalle spécifié, [max; min].

De plus, les nombres aléatoires doivent être uniformément répartis sur l’intervalle, non situés à un point particulier.

Currenly je génère comme:

for(int i=0; i<6; i++) { DWORD random = rand()%(max-min+1) + min; } 

De mes tests, des nombres aléatoires sont générés autour d’un point seulement.

 Example min = 3604607; max = 7654607; 

Nombre aléatoire généré:

 3631594 3609293 3630000 3628441 3636376 3621404 

De réponses ci-dessous: OK, RAND_MAX est 32767. Je suis sur la plate-forme Windows C ++. Existe-t-il une autre méthode pour générer des nombres aléatoires avec une dissortingbution uniforme?

Pourquoi le rand est une mauvaise idée

La plupart des réponses que vous avez obtenues ici utilisent la fonction rand et l’opérateur de module. Cette méthode peut ne pas générer de nombres uniformément (cela dépend de la plage et de la valeur de RAND_MAX ), et est donc déconseillée.

C ++ 11 et génération sur une plage

Avec C ++ 11, plusieurs autres options ont vu le jour. L’un d’entre eux correspond à vos besoins, pour générer un nombre aléatoire dans une plage, plutôt bien: std::uniform_int_dissortingbution . Voici un exemple:

 const int range_from = 0; const int range_to = 10; std::random_device rand_dev; std::mt19937 generator(rand_dev()); std::uniform_int_dissortingbution distr(range_from, range_to); std::cout < < distr(generator) << '\n'; 

Et voici l'exemple courant.

Autres générateurs aléatoires

L'en- tête offre d'innombrables autres générateurs de nombres aléatoires avec différents types de dissortingbutions, y compris Bernoulli, Poisson et normal.

Comment puis-je mélanger un conteneur?

Le standard fournit std::random_shuffle , qui peut être utilisé comme suit:

 std::vector vec = {4, 8, 15, 16, 23, 42}; std::random_device random_dev; std::mt19937 generator(random_dev()); std::shuffle(vec.begin(), vec.end(), generator); 

L'algorithme réorganisera les éléments de manière aléatoire, avec une complexité linéaire.

Boost.Random

Une autre alternative, si vous n'avez pas access à un compilateur C ++ 11 +, est d'utiliser Boost.Random . Son interface est très similaire à celle de C ++ 11.

[edit] Attention: N’utilisez pas rand() pour les statistiques, la simulation, la cryptographie ou quelque chose de grave.

C’est assez bon pour que les chiffres semblent aléatoires pour un humain typique pressé, pas plus.

Voir la réponse de @ Jefffrey pour de meilleures options, ou cette réponse pour les nombres aléatoires crypto-sécurisés.


En règle générale, les bits supérieurs affichent une meilleure dissortingbution que les bits faibles, de sorte que la méthode recommandée pour générer des nombres aléatoires d’une plage à des fins simples est la suivante:

 ((double) rand() / (RAND_MAX+1)) * (max-min+1) + min 

Note : assurez-vous que RAND_MAX + 1 ne déborde pas (merci Demi)!

La division génère un nombre aléatoire dans l’intervalle [0, 1); “étirer” cela à la plage requirejse. Ce n’est que lorsque max-min + 1 se rapproche de RAND_MAX que vous avez besoin d’une fonction “BigRand ()” telle que publiée par Mark Ransom.

Cela évite également certains problèmes de découpage dus au modulo, ce qui peut encore aggraver vos chiffres.


Le générateur de nombres aléatoires intégré ne garantit pas la qualité requirejse pour les simulations statistiques. Il est correct que les nombres soient “aléatoires” pour un humain, mais pour une application sérieuse, vous devriez prendre quelque chose de mieux – ou au moins vérifier ses propriétés (la dissortingbution uniforme est généralement bonne, mais les valeurs ont tendance à se corréler ). Knuth a un excellent (si difficile à lire) traité sur les générateurs de nombres aléatoires, et j’ai récemment trouvé que LFSR était excellent et simple à implémenter, étant donné que ses propriétés sont bonnes pour vous.

Si RAND_MAX est 32767, vous pouvez facilement doubler le nombre de bits.

 int BigRand() { assert(INT_MAX/(RAND_MAX+1) > RAND_MAX); return rand() * (RAND_MAX+1) + rand(); } 

J’aimerais compléter les excellentes réponses d’Angry Shoe et de peterchen par un bref aperçu de l’état de l’art en 2015:

Quelques bons choix

randutils

La bibliothèque randutils (présentation) est une nouveauté intéressante, offrant une interface simple et des capacités aléatoires (déclarées) robustes. Cela présente l’inconvénient d’append une dépendance à votre projet et, étant nouveau, il n’a pas été testé de manière approfondie. Quoi qu’il en soit, étant libre (licence MIT) et en-tête uniquement, je pense que cela vaut la peine d’essayer.

Échantillon minimal: un rouleau de filière

 #include  #include "randutils.hpp" int main() { randutils::mt19937_rng rng; std::cout < < rng.uniform(1,6) << "\n"; } 

Même si la bibliothèque ne l'intéresse pas, le site Web ( http://www.pcg-random.org/ ) contient de nombreux articles intéressants sur le thème de la génération de nombres aléatoires en général et de la bibliothèque C ++ en particulier.

Boost.Random

Boost.Random (documentation) est la bibliothèque qui a inspiré C ++ 11, avec qui partage une grande partie de l'interface. Bien que théoriquement une dépendance externe, Boost a désormais le statut de bibliothèque "quasi standard" et son module Random pourrait être considéré comme le choix classique pour la génération de nombres aléatoires de bonne qualité. Il présente deux avantages par rapport à la solution C ++ 11:

  • il est plus portable, il suffit de prendre en charge le compilateur pour C ++ 03
  • son random_device utilise des méthodes spécifiques au système pour offrir un semis de bonne qualité

Le seul petit défaut est que le module offrant random_device n'est pas uniquement en-tête, il faut comstackr et lier boost_random .

Échantillon minimal: un rouleau de filière

 #include  #include  #include  int main() { boost::random::random_device rand_dev; boost::random::mt19937 generator(rand_dev()); boost::random::uniform_int_dissortingbution<> distr(1, 6); std::cout < < distr(generator) << '\n'; } 

Bien que l’échantillon minimal fonctionne correctement, les programmes réels doivent utiliser deux améliorations:

  • faire mt19937 un thread_local : le générateur est assez dodu (> 2 Ko) et il vaut mieux ne pas l'allouer sur la stack
  • graine mt19937 avec plus d'un entier: le Mersenne Twister a un grand état et peut bénéficier de plus d'entropie lors de l'initialisation

Quelques mauvais choix

La bibliothèque C ++ 11

Tout en étant la solution la plus idiomatique, la bibliothèque n'offre pas beaucoup en échange de la complexité de son interface, même pour les besoins de base. La faille est dans std::random_device : le standard n'impose aucune qualité minimale pour sa sortie (tant que entropy() retourne 0 ) et, à partir de 2015, MinGW (pas le compilateur le plus utilisé, mais à peine un choix ésotérique) imprimera toujours 4 sur l'échantillon minimal.

Échantillon minimal: un rouleau de filière

 #include  #include  int main() { std::random_device rand_dev; std::mt19937 generator(rand_dev()); std::uniform_int_dissortingbution distr(1, 6); std::cout < < distr(generator) << '\n'; } 

Si l'implémentation n'est pas pourrie, cette solution doit être équivalente à celle de Boost, et les mêmes suggestions s'appliquent.

La solution de Godot

Échantillon minimal: un rouleau de filière

 #include  #include  int main() { std::cout < < std::randint(1,6); } 

C'est une solution simple, efficace et soignée. Seul défaut, il faudra du temps pour comstackr - environ deux ans, à condition que C ++ 17 soit sorti à temps et que la fonction expérimentale expérimentale soit approuvée dans le nouveau standard. Peut-être que d'ici là, les garanties sur la qualité de semis s'amélioreront.

La pire solution est la meilleure

Échantillon minimal: un rouleau de filière

 #include  #include  #include  int main() { std::srand(std::time(nullptr)); std::cout < < (std::rand() % 6 + 1); } 

L'ancienne solution C est considérée comme nuisible et pour de bonnes raisons (voir les autres réponses ici ou cette parsing détaillée ). Cependant, il a ses avantages: il est simple, portable, rapide et honnête, dans le sens où on sait que les nombres aléatoires qu'on obtient sont peu décents et que, par conséquent, on n'est pas tenté de les utiliser à des fins sérieuses.

La solution de troll comptable

Échantillon minimal: un rouleau de filière

 #include  int main() { std::cout < < 9; // http://dilbert.com/strip/2001-10-25 } 

Alors que 9 est un résultat quelque peu inhabituel pour un résultat normal, il faut admirer l'excellente combinaison des bonnes qualités de cette solution, qui parvient à être la plus rapide, la plus simple, la plus conviviale et la plus portable. En remplaçant 9 par 4, on obtient un générateur parfait pour n'importe quel type de donjons et de dragons, tout en évitant les valeurs 1, 2 et 3. Le seul défaut est que, en raison de la mauvaise humeur des trolls comptables de Dilbert, ce programme engendre en fait un comportement indéfini.

Si vous le pouvez, utilisez Boost . J’ai eu de la chance avec leur bibliothèque aléatoire .

uniform_int devrait faire ce que vous voulez.

Si vous êtes préoccupé par le caractère aléatoire et non par la vitesse, vous devez utiliser une méthode sécurisée de génération de nombres aléatoires. Il y a plusieurs façons de le faire … Le plus simple est d’utiliser le générateur de nombres aléatoires d’OpenSSL .

Vous pouvez également écrire votre propre en utilisant un algorithme de chiffrement (comme AES ). En choisissant une graine et une IV , puis en recodant continuellement la sortie de la fonction de cryptage. Utiliser OpenSSL est plus facile, mais moins viril.

Vous devriez regarder RAND_MAX pour votre compilateur / environnement particulier. Je pense que vous verriez ces résultats si rand () produit un nombre aléatoire de 16 bits. (vous semblez supposer que ce sera un nombre de 32 bits).

Je ne peux pas promettre que c’est la réponse, mais s’il vous plaît, affichez votre valeur de RAND_MAX, et un peu plus de détails sur votre environnement.

Vérifiez ce que RAND_MAX est sur votre système – je suppose que ce n’est que 16 bits, et que votre scope est trop grande pour cela.

Au-delà, voyez cette discussion sur: La génération d’entiers aléatoires dans une plage désirée et les notes sur l’utilisation (ou non) de la fonction C rand () .

Si vous voulez que les nombres soient uniformément répartis sur la plage, vous devez diviser votre intervalle en un nombre de sections égales qui représentent le nombre de points dont vous avez besoin. Ensuite, obtenez un nombre aléatoire avec un min / max pour chaque section.

Comme autre remarque, vous ne devriez probablement pas utiliser rand () car il n’est pas très bon pour générer des nombres aléatoires. Je ne sais pas quelle plate-forme vous utilisez, mais il y a probablement une meilleure fonction que vous pouvez appeler comme random ().

Ce n’est pas le code, mais cette logique peut vous aider.

 static double rnd(void) { return (1.0/(RAND_MAX+1.0)*((double)(rand())) ); } static void InitBetterRnd(unsigned int seed) { register int i; srand( seed ); for( i=0; i 

Cela devrait permettre une dissortingbution uniforme sur la plage [low, high) sans utiliser de flottants, tant que la plage globale est inférieure à RAND_MAX.

 uint32_t rand_range_low(uint32_t low, uint32_t high) { uint32_t val; // only for 0 < range <= RAND_MAX assert(low < high); assert(high - low <= RAND_MAX); uint32_t range = high-low; uint32_t scale = RAND_MAX/range; do { val = rand(); } while (val >= scale * range); // since scale is truncated, pick a new val until it's lower than scale*range return val/scale + low; } 

et pour les valeurs supérieures à RAND_MAX, vous voulez quelque chose comme

 uint32_t rand_range(uint32_t low, uint32_t high) { assert(high>low); uint32_t val; uint32_t range = high-low; if (range < RAND_MAX) return rand_range_low(low, high); uint32_t scale = range/RAND_MAX; do { val = rand() + rand_range(0, scale) * RAND_MAX; // scale the initial range in RAND_MAX steps, then add an offset to get a uniform interval } while (val >= range); return val + low; } 

C’est à peu près comment std :: uniform_int_dissortingbution fait les choses.

De par leur nature, un petit échantillon de nombres aléatoires ne doit pas nécessairement être dissortingbué uniformément. Ils sont aléatoires, après tout. Je suis d’accord que si un générateur de nombres aléatoires génère des nombres qui semblent toujours être groupés, alors il y a probablement quelque chose qui ne va pas.

Mais gardez à l’esprit que le hasard n’est pas nécessairement uniforme.

Edit: J’ai ajouté “petit échantillon” pour clarifier.

La solution donnée par man 3 rand pour un nombre compris entre 1 et 10 inclus est la suivante:

 j = 1 + (int) (10.0 * (rand() / (RAND_MAX + 1.0))); 

Dans votre cas, ce serait:

 j = min + (int) ((max-min+1) * (rand() / (RAND_MAX + 1.0))); 

Bien sûr, ce n’est pas un hasard ou une uniformité parfaits, comme le soulignent certains autres messages, mais cela est suffisant dans la plupart des cas.

@Solution ((double) rand() / (RAND_MAX+1)) * (max-min+1) + min

Attention : N’oubliez pas, à cause des étirements et des erreurs de précision possibles (même si RAND_MAX était suffisamment grand), vous ne pourrez générer que des “bacs” dissortingbués uniformément et pas tous les nombres dans [min, max].


@Solution: Bigrand

Attention : Notez que cela double les bits, mais ne sera toujours pas capable de générer tous les nombres de votre plage en général, c’est-à-dire qu’il n’est pas forcément vrai que BigRand () générera tous les nombres entre ses plages.


Info : Votre approche (modulo) est “fine” tant que la plage de rand () dépasse votre intervalle d’intervalle et rand () est “uniforme”. L’erreur pour au plus les premiers nombres max-min est 1 / (RAND_MAX +1).

Aussi, je suggère de passer au nouveau paquetage aléatoire e en C ++ 11, qui offre de meilleures et plus de variétés d’implémentations que rand ().

Je viens de trouver cela sur Internet. Cela devrait fonctionner:

 DWORD random = ((min) + rand()/(RAND_MAX + 1.0) * ((max) - (min) + 1));