Pourquoi C ++ rand () semble-t-il générer uniquement des nombres du même ordre de grandeur?

146

Dans une petite application écrite en C / C ++, je suis confronté à un problème avec la randfonction et peut-être la graine:

Je veux produire une séquence de nombres aléatoires qui sont d'ordres différents, c'est-à-dire avec des valeurs de logarithme différentes (base 2). Mais il semble que tous les nombres produits sont du même ordre, oscillant juste entre 2 ^ 25 et 2 ^ 30.

Est-ce parce que le temps rand()est basé sur Unix qui est maintenant un nombre relativement important? Qu'est-ce que j'oublie? Je ne seme rand()qu'une seule fois au début du main().

Tallaron Mathias
la source
7
FWIW donc, est-ce C ou C ++? Si par C / C ++ vous voulez dire que vous pouvez réellement utiliser C ++, et que la mention de C était juste aléatoire, peut-être que cela en.cppreference.com/w/cpp/numeric/random/binomial_distribution peut vous aider.
R. Martinho Fernandes
9
Malheureusement, vous pariez sur le mauvais cheval. Les semences ne devraient pas être votre problème. Votre problème était une mauvaise distribution attendue. Étant donné que le programmeur impartial s'attendrait rand()à renvoyer des nombres uniformément distribués (la documentation avec un classement Google élevé le dit explicitement), je ne pense pas que cette question soit utile pour les futurs lecteurs. C'est pourquoi voter contre mais ne laissez pas cela vous décourager d'utiliser SO.
Emperor Orionii
12
@ doug65536 "... où aucun nombre n'est jamais répété" - ce n'est pas aléatoire! Je pourrais financer ma retraite à la table de craps si mes dés rand () ne renvoyaient jamais le même numéro deux fois jusqu'à ce que chaque numéro possible soit retourné.
Chris Gregg
6
@GalacticCowboy Ne confondez pas la périodicité avec une répétition de nombres individuels. D'après l'article de Wikipédia que vous avez cité: "un résultat répété n'implique pas que la fin de la période a été atteinte, car son état interne peut être plus grand que sa production." Ce serait très, très mauvais si un PRNG produisait une valeur et avait la garantie de ne plus produire cette valeur jusqu'à ce que toutes les valeurs soient renvoyées.
Chris Gregg
12
Doug65536, personne ne se bat. Ils disent simplement que vous vous trompez. Un PRNG pourrait très bien produire ce qui suit si je voulais un RAND entre 1 et 10: 2 4 7 2 8 1 5 9 7 3 Ce serait tout à fait valable, malgré les multiples 2 et 7. Je pense que vous confondez le PRNG avec la fonction de lecture aléatoire de votre iPhone.
Relaxing In Cyprus

Réponses:

479

Il n'y a que 3% des nombres entre 1 et 2 30 qui ne sont PAS entre 2 25 et 2 30 . Donc, cela semble assez normal :)

Parce que 2 25 /2 30 = 2 -5 = 1/32 = 0,03125 = 3,125%

C4stor
la source
36
Oui, bon point! Il y a 31 fois plus de nombres entre 2 ^ 25 et 2 ^ 30 qu'entre 1 et 2 ^ 25 :) merci pour la réponse rapide. Je dois alors repenser le programme. Réponse à la question.
Tallaron Mathias
1
@TallaronMathias Envisagez de tronquer le nombre par >>décalage de bits - cela vous donnera des nombres plus petits. (Ou en prenant un module avec %.)
Sean Allred
13
Je m'attendrais à ce que cela soit évident pour la plupart des programmeurs: tout entier non signé inférieur à 2 ^ 25 doit avoir ses 7 premiers bits égaux à 0- et si chaque bit est aléatoire ...
BlueRaja - Danny Pflughoeft
118
@ BlueRaja-DannyPflughoeft - si les probabilités étaient évidentes, les casinos seraient en faillite.
Brett Hale
26
@BrettHale - Je ne pense pas que les programmeurs soient la cible démographique d'un casino.
EkoostikMartin
272

Le vert plus clair est la région entre 0 et 2 25 ; le vert plus foncé est la région entre 2 25 et 2 30 . Les tiques sont des puissances de 2.

Distribution

Casey Chu
la source
42

Vous devez être plus précis: vous voulez des valeurs de logarithme de base 2 différentes mais quelle distribution voulez-vous pour cela? Les fonctions standard rand () génèrent une distribution uniforme, vous devrez transformer cette sortie en utilisant la fonction quantile associée à la distribution que vous voulez.

Si vous nous indiquez la distribution, nous pouvons vous indiquer la quantilefonction dont vous avez besoin.

Bathsheba
la source
13
+1, la distribution est le terme crucial. Cela n'a pas vraiment de sens de parler de nombres aléatoires quand on ne sait rien de la distribution. L'uniforme n'est qu'un cas particulier, quoique important. Cela pourrait être un bon endroit pour signaler diverses distributions de la bibliothèque standard C ++ 11.
gauche autour du
18

Si vous voulez des ordres de grandeur différents, pourquoi ne pas simplement essayer pow(2, rand())? Ou peut-être choisir l'ordre directement comme rand (), comme l'a suggéré Harold?

aspirant_sarge
la source
3
bonne idée, mais vous devriez corriger votre réponse en utilisant pow au lieu de ^ (qui est l'opérateur logique xor, pas power, en langage C).
kriss
6
Puisque rand()peut aller jusqu'à RAND_MAX, vous devez vraiment mettre à l'échelle votre nombre aléatoire pour que le résultat ne déborde pas ...
Floris
@Floris: mais si vous mettez à l'échelle une petite plage dénombrable sur une très grande plage, vous aurez BEAUCOUP de trous, ce qui n'est probablement pas ce à quoi OP s'attend.
André Caron
13

@ C4stor a fait un excellent point. Mais, pour un cas plus général et plus facile à comprendre pour l'homme (base 10): pour la plage de 1 à 10 ^ n, ~ 90% des nombres vont de 10 ^ (n-1) à 10 ^ n, donc, ~ 99% des nombres vont de 10 ^ (n-2) à 10 ^ n. Continuez à ajouter autant de décimales que vous le souhaitez.

Mathématiques amusantes, si vous continuez à faire cela pour n, vous pouvez voir que de 1 à 10 ^ n, 99,9999 ...% = 100% des nombres vont de 10 ^ 0 à 10 ^ n avec cette méthode.

Maintenant à propos du code, si vous voulez un nombre aléatoire avec des ordres de grandeur aléatoires, de 0 à 10 ^ n, vous pouvez faire:

  1. Génère un petit nombre aléatoire de 0 à n

  2. Si vous connaissez la plage de n, générez un grand nombre aléatoire d'ordre 10 ^ k où k> max {n}.

  3. Coupez le nombre aléatoire le plus long pour obtenir les n chiffres de ce grand nombre aléatoire.

Francisco Presencia
la source
46
Vous avez tout à fait raison, mais pour une réponse VRAIMENT facile à comprendre, l'OP devrait se demander pourquoi 90% des nombres aléatoires entre 1 et 100 sont à deux chiffres.
Renseignez-vous sur Monica le
13

La réponse basique (et correcte) a déjà été donnée et acceptée ci-dessus: il y a 10 nombres entre 0 et 9, 90 nombres entre 10 et 99, 900 entre 100 et 999, etc.

Pour un moyen efficace en termes de calcul d'obtenir une distribution avec une distribution approximativement logarithmique, vous voulez décaler à droite votre nombre aléatoire d'un nombre aléatoire:

s = rand() & 31; // a random number between 0 and 31 inclusive, assuming RAND_MAX = 2^32-1
r = rand() >> s; // right shift

Ce n'est pas parfait, mais c'est beaucoup plus rapide que l'informatique pow(2, rand()*scalefactor). Elle sera "grumeleuse" en ce sens que la distribution sera uniforme pour les nombres à l'intérieur d'un facteur 2 (uniforme pour 128 à 255, moitié de la densité pour 256 à 1023, etc.).

Voici un histogramme de la fréquence des nombres de 0 à 31 (en 1M d'échantillons):

entrez la description de l'image ici

Floris
la source
nitpick: cela encourage de très petits nombres plus que ce à quoi on pourrait s'attendre. La probabilité d'obtenir un zéro est significativement plus élevée qu'un 10.
Mooing Duck
Eh bien, le but est d'encourager les petits nombres, donc je suis content que cela fonctionne! J'ai exécuté une simulation de Monte Carlo, et cela me donne une baisse de probabilité d'un facteur 2 lorsque les nombres doublent - un peu comme une distribution logarithmique. Réponse mise à jour avec une image.
Floris
non, je veux dire, avec rand()>>(rand()&31);, on s'attendrait intuitivement à ce que 1/32 des nombres ait 32 bits, et 1 / 32e des nombres ait 31 bits, et 1 / 32e des nombres ait 30 bits, etc. Mais c'est pas les résultats que vous obtenez, seulement environ 1 / 64e des nombres donneraient 32 bits, tandis que près de la moitié devrait être 0. Puisque mes calculs mentaux ne sont pas d'accord avec vos mesures, je vais devoir faire mes propres mesures pour comprendre cette sortie.
Mooing Duck
2
Je ne veux pas dire que votre code est erroné. C'est probablement ce que je ferais. Cela mérite juste un avertissement que les résultats ne sont pas tout à fait distribués comme on pourrait s'y attendre.
Mooing Duck
1
Je pense que le problème vient du fait de penser à 0 comme un nombre de 1 bit ... c'est le genre d'énigme que vous rencontrez lorsque vous mélangez des entiers et des logarithmes. Cela a été un bon exercice et vous m'avez donné matière à réflexion. "Testez les limites de votre algorithme" - il ne vieillit jamais.
Floris
5

Il y a exactement le même nombre de nombres entre 0 et 2 ^ 29 et 2 ^ 29 et 2 ^ 30.

Une autre façon de voir le problème: considérez la représentation binaire du nombre aléatoire que vous générez, la probabilité que le bit le plus élevé soit 1 égale 1/2, et, par conséquent, vous obtenez l'ordre 29 dans la moitié des cas. Ce que vous voulez, c'est voir un nombre qui serait inférieur à 2 ^ 25, mais cela signifie que 5 bits les plus élevés sont tous à zéro, ce qui se produit avec une faible probabilité de 1/32. Il y a de fortes chances que même si vous l'exécutez pendant une longue période, vous ne verrez jamais du tout l'ordre en dessous de 15 (la probabilité est quelque chose comme rouler 6 6 fois de suite).

Maintenant, la partie de votre question sur la graine. Non, la graine ne peut pas déterminer la plage à partir de laquelle les nombres sont générés, elle détermine simplement le premier élément initial. Pensez à rand () comme une séquence de tous les nombres possibles dans la plage (permutation prédéterminée). La valeur de départ détermine où vous commencez à dessiner des nombres à partir de la séquence. C'est pourquoi si vous voulez un (pseudo) hasard, vous utilisez l'heure actuelle pour initialiser la séquence: vous ne vous souciez pas que la position à partir de laquelle vous partez ne soit pas uniformément répartie, tout ce qui compte c'est que vous ne partez jamais de la même position.

Vadim
la source
2

l'utiliser pow(2,rand()) vous donnera les réponses dans l'ordre de grandeur désirée !!

Shivendra
la source
2

Si vous souhaitez utiliser des nombres aléatoires à partir d'un service en ligne, vous pouvez utiliser wget pour cela, vous voudrez peut-être voir que vous pouvez également utiliser des services comme random.org pour votre génération de nombres aléatoires, vous pouvez les attraper en utilisant wget, puis en lisant les nombres à partir de le fichier téléchargé

wget -q https://www.random.org/integers/?num=100&min=1&max=100&col=5&base=10&format=html&rnd=new -O new.txt

http://programmingconsole.blogspot.in/2013/11/a-better-and-different-way-to-generate.html

Namit Sinha
la source
Bienvenue à SO. veuillez vous abstenir de publier des liens comme réponses. Vous pouvez fournir un croquis détaillé d'une réponse en laissant les détails à lire via des liens.
Shai