7
votes

Devrais-je utiliser "Rand% n" ou "Rand () / (rand_max / n + 1)"?

Je lisais le C FAQ et découvert dans un question qu'elle me recommande d'utiliser rand () / (rand_max / n + 1) code> au lieu de la manière la plus populaire" code> rand () % N code>.

Le raisonnement pour cela est que lorsque n code> est un nombre bas rand ()% n code> n'utilisera que quelques bits de rand () code>. p>

J'ai testé les différentes approches avec n code> étant 2 code> sur Windows et Linux mais ne pouvait pas remarquer une différence . p> xxx pré>

La sortie est ceci (sur ma machine GNU / Linux): P>

rand() % N:
1 0 1 1 1 1 0 0 1 1 0 1 0 1 1 0 0 0 0 0 1 0 1 1 0 0 0 1 1 1 1 0 0 0 1 1 1 0 1 0 
rand() / (RAND_MAX / N + 1):
1 0 1 1 1 0 0 1 0 1 0 1 0 1 1 1 1 1 0 1 0 0 0 1 0 0 0 0 1 0 1 1 1 0 1 1 0 1 0 1 


2 commentaires

Ce n'est pas la façon dont vous remarquez des différences dans ce scénario. Votre erreur était ceci: "Les deux alternatives semblent parfaitement aléatoires pour moi" . Vous avez besoin d'une approche analytique. Pensez-y comme ceci: le seul moyen de vraiment mesurer cela serait d'obtenir des statistiques sur une séquence infiniment longue.


Ceci est un mauvais exemple avec #define n 2 car rand_max est un nombre impair et rand ()% n retournera une distribution égale de 0 et 1 compte tenu des limitations du générateur pseudo aléatoire. Il n'y a absolument aucune différence dans les deux méthodes ici, uniquement dans la séquence des résultats pseudo-aléatoires. Mais (en général) le plus grand est n , plus le biais de modulo est important.


3 Réponses :


6
votes

si n est une puissance de deux, l'utilisation de la technique de reste est généralement sûre ( rand_max est généralement une puissance de deux moins 1, la plage complète a donc une puissance. de deux longueurs). Plus généralement, n doit diviser la plage de rand () afin d'éviter le biais.

Sinon, vous rencontrez dans Ce problème , quelle que soit la qualité de rand () . En bref, le problème est que vous découpez que vous coupez cette plage dans un certain nombre de "parties" chacune de la longueur n , si n ne divise pas la plage puis la dernière partie ne sera pas complet. Les chiffres qui ont "coupé" de cette partie sont donc moins susceptibles de se produire, car ils ont une "pièce" en moins, ils peuvent être générés à partir de.

Malheureusement, rand () / (rand_max / n + 1) est également cassé (de la même manière), la réponse réelle est donc: n'utilisez pas d'entre eux.

Le problème décrit ci-dessus est vraiment fondamental, il n'existe aucun moyen de distribuer uniformément des valeurs différentes sur les résultats de y à moins que y divise X. Vous pouvez le réparer en rejetant une partie des échantillons aléatoires, pour faire diviser y le nouveau x .


1 commentaires

Envisagez d'ajouter un bref résumé du problème ici, car les liens peuvent mourir.



4
votes

Il y a un autre problème avec rand ()% n qui introduit un biais de modulo.

Pour SIMPLICITY'S SAKE ''s Faire semblant Rand_max est de 7 et n est 6. Vous voulez que les numéros 0, 1, 2, 3, 4, 5 doivent apparaître dans le flux aléatoire avec une probabilité égale. Cependant, 0 et 1 apparaîtront 1/4 du temps et les autres numéros seulement 1/8 du temps car 6 et 7 ont des restes 0 et 1 respectivement. Vous devez utiliser l'autre méthode, mais soigneusement car la troncature des fractions pourrait introduire un problème similaire.

Si vous avez arc4random () , vous pouvez utiliser arc4random_uniform () pour réaliser une distribution impartiale sans avoir à faire attention.


2 commentaires

@David Eisenstat Les nombres aléatoires ne sont pas des distributions uniformes, mais un échantillon très important devrait l'approcher statistiquement.


Oui. La troncature des fractions pourrait introduire un problème similaire. Mais cela est plus répandu. La première approche préjugée à des chiffres plus faibles, tandis que la deuxième approche préjeter vers des nombres répartis de manière uniforme dans la plage, ce qui pourrait constituer un meilleur comportement pour certaines applications.



0
votes

sur AVR-GCC:

J'utilisais rand () & 0xff pour obtenir un nombre aléatoire de 0 à 255 et les résultats n'étaient pas bons. Il s'est avéré que l'utilisation de bits inférieurs n'est pas une méthode très fiable, souvent les mêmes valeurs. Pourrait être similaire avec modulo.

rand () / (rand_max / n + 1) a travaillé beaucoup mieux pour moi


1 commentaires

Si vous êtes inquiet du hasard et de bons résultats: la page man sur mon Mac décrit rand comme "générateur de nombres aléatoires". Voir Qu'est-ce qu'un remplacement approprié pour Rand ()? pour des alternatives.