J'ai besoin d'une fonction qui générerait un entier aléatoire dans une plage donnée (y compris les valeurs de bordure). Je n'ai pas d'exigences de qualité / aléatoire déraisonnables, j'ai quatre exigences:
- J'ai besoin que ce soit rapide. Mon projet doit générer des millions (voire des dizaines de millions) de nombres aléatoires et ma fonction de générateur actuelle s'est avérée être un goulot d'étranglement.
- J'ai besoin qu'il soit raisonnablement uniforme (l'utilisation de rand () est parfaitement bien).
- les plages min-max peuvent être n'importe quoi de <0, 1> à <-32727, 32727>.
- il doit pouvoir être semé.
J'ai actuellement le code C ++ suivant:
output = min + (rand() * (int)(max - min) / RAND_MAX)
Le problème est qu'il n'est pas vraiment uniforme - max est renvoyé uniquement lorsque rand () = RAND_MAX (pour Visual C ++, il est 1/32727). C'est un problème majeur pour les petites plages telles que <-1, 1>, où la dernière valeur n'est presque jamais retournée.
J'ai donc pris un stylo et du papier et suis venu avec la formule suivante (qui s'appuie sur l'astuce d'arrondi entier (int) (n + 0.5)):
Mais cela ne me donne toujours pas une distribution uniforme. Des analyses répétées avec 10000 échantillons me donnent un rapport de 37:50:13 pour les valeurs -1, 0. 1.
Pouvez-vous suggérer une meilleure formule? (ou même fonction de générateur de nombres pseudo-aléatoires entiers)
Réponses:
Une solution distribuée rapide, un peu meilleure que la vôtre, mais toujours pas correctement uniforme est
Sauf lorsque la taille de la plage est une puissance de 2, cette méthode produit des nombres distribués non uniformes biaisés quelle que soit la qualité de
rand()
. Pour un test complet de la qualité de cette méthode, veuillez lire ceci .la source
rand()
devrait être considéré comme dangereux en C ++, il existe de bien meilleures façons d'obtenir quelque chose qui est uniformément distribué et en fait aléatoire.La réponse C ++ la plus simple (et donc la meilleure) (en utilisant la norme 2011) est
Pas besoin de réinventer la roue. Pas besoin de s'inquiéter des préjugés. Pas besoin de s'inquiéter d'utiliser le temps comme une graine aléatoire.
la source
random_device
ce qui pourrait être complètement enfreint dans certains cas . De plus,mt19937
bien qu'un très bon choix d'usage général, ce n'est pas le plus rapide des générateurs de bonne qualité (voir cette comparaison ) et pourrait donc ne pas être le candidat idéal pour l'OP.minstd
sera à l'avenir ), mais c'est un progrès. Quant à la mauvaise mise en œuvrerandom_device
- c'est horrible et devrait être considéré comme un bogue (peut-être aussi du standard C ++, si cela le permet).rand()
n'est pas une option, et est-ce important pour une utilisation non critique, comme la génération d'un index pivot aléatoire? De plus, dois-je me soucier de la construction derandom_device
/mt19937
/uniform_int_distribution
dans une boucle serrée / une fonction intégrée? Dois-je préférer les faire circuler?Si votre compilateur prend en charge C ++ 0x et que son utilisation est une option pour vous, le nouvel en-
<random>
tête standard répondra probablement à vos besoins. Il a une haute qualitéuniform_int_distribution
qui acceptera les limites minimales et maximales (incluses selon vos besoins), et vous pouvez choisir parmi divers générateurs de nombres aléatoires pour vous connecter à cette distribution.Voici le code qui génère un million de
int
s aléatoires uniformément répartis dans [-57, 365]. J'ai utilisé les nouvelles<chrono>
installations std pour chronométrer, car vous avez mentionné que les performances sont une préoccupation majeure pour vous.Pour moi (Intel Core i5 2,8 GHz), cela imprime:
2.10268e + 07 nombres aléatoires par seconde.
Vous pouvez amorcer le générateur en passant un int à son constructeur:
Si vous trouvez plus tard que
int
cela ne couvre pas la plage dont vous avez besoin pour votre distribution, cela peut être corrigé en changeant launiform_int_distribution
même chose (par exemple enlong long
):Si vous constatez plus tard que le
minstd_rand
générateur n'est pas de qualité suffisante, il peut également être facilement remplacé. Par exemple:Avoir un contrôle séparé sur le générateur de nombres aléatoires et la distribution aléatoire peut être assez libérateur.
J'ai également calculé (non montré) les 4 premiers "moments" de cette distribution (en utilisant
minstd_rand
) et les ai comparés aux valeurs théoriques pour tenter de quantifier la qualité de la distribution:(Le
x_
préfixe fait référence à "attendu")la source
d
à chaque itération avec des limites différentes? Dans quelle mesure cela ralentirait-il la boucle?Divisons le problème en deux parties:
n
entre 0 et (max-min).La première partie est évidemment la plus difficile. Supposons que la valeur de retour de rand () soit parfaitement uniforme. L'utilisation de modulo ajoutera un biais aux premiers
(RAND_MAX + 1) % (max-min+1)
nombres. Donc , si nous pouvions changer comme par magieRAND_MAX
àRAND_MAX - (RAND_MAX + 1) % (max-min+1)
, il n'y aurait plus aucun parti pris.Il s'avère que nous pouvons utiliser cette intuition si nous sommes prêts à autoriser le pseudo-non-déterminisme dans le temps d'exécution de notre algorithme. Chaque fois que rand () renvoie un nombre trop grand, nous demandons simplement un autre nombre aléatoire jusqu'à ce que nous en obtenions un qui soit assez petit.
Le temps d'exécution est maintenant géométriquement distribué , avec la valeur attendue
1/p
oùp
est la probabilité d'obtenir un nombre suffisamment petit au premier essai. PuisqueRAND_MAX - (RAND_MAX + 1) % (max-min+1)
est toujours inférieur à(RAND_MAX + 1) / 2
, nous le savonsp > 1/2
, donc le nombre d'itérations attendu sera toujours inférieur à deux pour n'importe quelle plage. Il devrait être possible de générer des dizaines de millions de nombres aléatoires en moins d'une seconde sur un processeur standard avec cette technique.ÉDITER:
Bien que ce qui précède soit techniquement correct, la réponse de DSimon est probablement plus utile dans la pratique. Vous ne devriez pas implémenter cela vous-même. J'ai vu beaucoup d'implémentations d'échantillonnage de rejet et il est souvent très difficile de voir si c'est correct ou non.
la source
Et le Mersenne Twister ? L'implémentation Boost est plutôt facile à utiliser et est bien testée dans de nombreuses applications du monde réel. Je l'ai moi-même utilisé dans plusieurs projets académiques tels que l'intelligence artificielle et les algorithmes évolutifs.
Voici leur exemple où ils créent une fonction simple pour lancer un dé à six faces:
Oh, et voici un peu plus de proxénétisme de ce générateur juste au cas où vous ne seriez pas convaincu que vous devriez l'utiliser sur le bien inférieur
rand()
:la source
boost::uniform_int
distribution), vous pouvez transformer les gammes min max en ce que vous voulez, et c'est semable.Il s'agit d'un mappage de 32768 entiers en (nMax-nMin + 1) entiers. Le mappage sera assez bon si (nMax-nMin + 1) est petit (comme dans votre exigence). Notez cependant que si (nMax-nMin + 1) est grand, le mappage ne fonctionnera pas (par exemple, vous ne pouvez pas mapper 32768 valeurs à 30000 valeurs avec une probabilité égale). Si de telles plages sont nécessaires, vous devez utiliser une source aléatoire 32 bits ou 64 bits, au lieu de rand () 15 bits, ou ignorer les résultats rand () qui sont hors limites.
la source
RAND_MAX
en(double) RAND_MAX
pour éviter l'avertissement de dépassement d'entier.Voici une version non biaisée qui génère des nombres dans
[low, high]
:Si votre plage est raisonnablement petite, il n'y a aucune raison de mettre en cache le côté droit de la comparaison dans la
do
boucle.la source
[0, h)
pour la simplicité. L'appelrand()
aRAND_MAX + 1
des valeurs de retour possibles; prenantrand() % h
effondrements(RAND_MAX + 1) / h
d'entre eux à chacune desh
valeurs de sortie, à l' exception que(RAND_MAX + 1) / h + 1
d'entre eux sont mis en correspondance avec les valeurs qui sont inférieures(RAND_MAX + 1) % h
( à cause de la dernière période partielle à travers lesh
sorties). Nous supprimons donc(RAND_MAX + 1) % h
les sorties possibles pour obtenir une distribution non biaisée.Je recommande la bibliothèque Boost.Random , elle est très détaillée et bien documentée, vous permet de spécifier explicitement la distribution que vous voulez, et dans les scénarios non cryptographiques, elle peut en fait surpasser une implémentation typique de la bibliothèque C.
la source
supposons que min et max sont des valeurs int, [et] signifie inclure cette valeur, (et) signifie ne pas inclure cette valeur, en utilisant ci-dessus pour obtenir la bonne valeur en utilisant c ++ rand ()
référence: pour () [] définir, visitez:
https://en.wikipedia.org/wiki/Interval_(mathematics)
pour la fonction rand et srand ou la définition RAND_MAX, visitez:
http://en.cppreference.com/w/cpp/numeric/random/rand
[min max]
(min max]
[min max)
(min max)
la source
Dans ce thread, l'échantillonnage de rejet a déjà été discuté, mais je voulais suggérer une optimisation basée sur le fait qu'elle
rand() % 2^something
n'introduit aucun biais comme déjà mentionné ci-dessus.L'algorithme est vraiment simple:
Voici mon exemple de code:
Cela fonctionne bien en particulier pour les petits intervalles, car la puissance de 2 sera "plus proche" de la longueur réelle de l'intervalle, et donc le nombre d'échecs sera plus petit.
PS
Évidemment, éviter la récursivité serait plus efficace (pas besoin de calculer au-delà du plafond du journal ..) mais j'ai pensé que c'était plus lisible pour cet exemple.
la source
Notez que dans la plupart des suggestions, la valeur aléatoire initiale que vous avez obtenue de la fonction rand (), qui est généralement de 0 à RAND_MAX, est simplement gaspillée. Vous n'en créez qu'un seul nombre aléatoire, alors qu'il existe une procédure sonore qui peut vous en donner plus.
Supposons que vous vouliez une région [min, max] de nombres aléatoires entiers. Nous partons de [0, max-min]
Prendre la base b = max-min + 1
Commencez par représenter un nombre obtenu de rand () en base b.
De cette façon, vous avez floor (log (b, RAND_MAX)) parce que chaque chiffre de la base b, sauf peut-être le dernier, représente un nombre aléatoire dans la plage [0, max-min].
Bien sûr, le décalage final vers [min, max] est simple pour chaque nombre aléatoire r + min.
Si NUM_DIGIT est le nombre de chiffres en base b que vous pouvez extraire et c'est
alors ce qui précède est une implémentation simple d'extraction de NUM_DIGIT nombres aléatoires de 0 à b-1 à partir d'un nombre aléatoire RAND_MAX fournissant b <RAND_MAX.
la source
La formule pour cela est très simple, alors essayez cette expression,
la source
int num = (int) rand() % (max - min) + min;
L'expression suivante doit être impartiale si je ne me trompe pas:
Je suppose ici que rand () vous donne une valeur aléatoire comprise entre 0,0 et 1,0 NON compris 1,0 et que max et min sont des entiers avec la condition que min <max.
la source
std::floor
renvoiedouble
, et nous avons besoin d'une valeur entière ici. Je voudrais simplement lancer auint
lieu d'utiliserstd::floor
.