web-dev-qa-db-fra.com

Pourquoi Rand () + Rand () produisent-ils des nombres négatifs?

J'ai observé que la fonction Rand() de la bibliothèque, lorsqu'elle est appelée une seule fois dans une boucle, produit presque toujours des nombres positifs.

for (i = 0; i < 100; i++) {
    printf("%d\n", Rand());
}

Mais lorsque j'ajoute deux appels Rand(), les numéros générés ont désormais davantage de numéros négatifs.

for (i = 0; i < 100; i++) {
    printf("%d = %d\n", Rand(), (Rand() + Rand()));
}

Quelqu'un peut-il expliquer pourquoi je vois des nombres négatifs dans le second cas?

PS: J'initialise la graine avant la boucle en tant que srand(time(NULL)).

303
badmad

Rand() est défini pour renvoyer un entier compris entre 0 et Rand_MAX.

Rand() + Rand()

pourrait déborder. Ce que vous observez est probablement le résultat de comportement non défini provoqué par un dépassement d'entier.

540
P.P.

Le problème est l'addition. Rand() renvoie une valeur int de 0...Rand_MAX. Donc, si vous en ajoutez deux, vous obtiendrez Rand_MAX * 2. Si cela dépasse INT_MAX, le résultat de l'addition déborde de la plage valide qu'une int peut contenir. Le dépassement des valeurs signées est un comportement indéfini et peut amener votre clavier à vous parler en langues étrangères.

Comme il n’ya aucun avantage à ajouter deux résultats aléatoires, l’idée simple est de ne pas le faire. Vous pouvez également convertir chaque résultat en unsigned int avant l'addition, si cela peut contenir la somme. Ou utilisez un type plus gros. Notez que long n'est pas nécessairement plus large que int, il en va de même pour long long si int vaut au moins 64 bits!

Conclusion: juste éviter l'addition. Il ne fournit pas plus de "hasard". Si vous avez besoin de plus de bits, vous pouvez concaténer les valeurs sum = a + b * (Rand_MAX + 1), mais cela nécessite également un type de données plus grand que int.

La raison que vous avez indiquée est d’éviter un résultat nul: cela ne peut être évité en ajoutant les résultats de deux appels Rand(), car les deux peuvent être nuls. Au lieu de cela, vous pouvez simplement augmenter. Si Rand_MAX == INT_MAX, cela ne peut pas être fait dans int. Cependant, (unsigned int)Rand() + 1 fera très, très probablement. Probablement (pas définitivement), car il nécessite UINT_MAX > INT_MAX, ce qui est vrai pour toutes les implémentations dont je suis au courant (qui couvre pas mal d'architectures intégrées, de DSP et toutes les plates-formes de bureau, mobiles et serveurs des 30 dernières années) .

Avertissement:

Bien que vous ayez déjà ajouté des commentaires ici, veuillez noter que l’ajout de deux valeurs aléatoires ne donne pas une distribution uniforme, mais une distribution triangulaire comme celle consistant à lancer deux dés: obtenir 12 ( deux dés) les deux dés doivent montrer 6. pour 11, il existe déjà deux variantes possibles: 6 + 5 ou 5 + 6, etc.

Donc, l'addition est également mauvaise sous cet aspect.

Notez également que les résultats générés par Rand() ne sont pas indépendants les uns des autres, car ils sont générés par un générateur de nombres pseudo-aléatoires . Notez également que la norme ne spécifie pas la qualité ou la distribution uniforme des valeurs calculées.

88

Ceci est une réponse à une clarification de la question faite dans le commentaire de cette réponse ,

la raison pour laquelle j'étais en train d'ajouter était d'éviter "0" comme nombre aléatoire dans mon code. Rand () + Rand () était la solution rapide et sale qui m'est venue à l’esprit.

Le problème était d'éviter 0. La solution proposée présente au moins deux problèmes. L'une est, comme les autres réponses l'indiquent, que Rand()+Rand() peut invoquer un comportement indéfini. Le meilleur conseil est de ne jamais invoquer un comportement indéfini. Un autre problème est qu'il n'y a aucune garantie que Rand() ne produira pas 0 deux fois de suite.

Le rejet de zéro suivant évite les comportements indéfinis et, dans la grande majorité des cas, sera plus rapide que deux appels à Rand():

int rnum;
for (rnum = Rand(); rnum == 0; rnum = Rand()) {}
// or do rnum = Rand(); while (rnum == 0);
35
David Hammen

Fondamentalement, Rand() produit des nombres compris entre 0 et Rand_MAX, et 2 Rand_MAX > INT_MAX dans votre cas.

Vous pouvez moduler avec la valeur maximale de votre type de données pour éviter tout débordement. Bien sûr, cela va perturber la distribution des nombres aléatoires, mais Rand n'est qu'un moyen d'obtenir des nombres aléatoires rapides.

#include <stdio.h>
#include <limits.h>

int main(void)
{
    int i=0;

    for (i=0; i<100; i++)
        printf(" %d : %d \n", Rand(), ((Rand() % (INT_MAX/2))+(Rand() % (INT_MAX/2))));

    for (i=0; i<100; i++)
        printf(" %d : %ld \n", Rand(), ((Rand() % (LONG_MAX/2))+(Rand() % (LONG_MAX/2))));

    return 0;
}
3
Khaled.K

Peut-être pourriez-vous essayer une approche plutôt délicate en vous assurant que la valeur renvoyée par la somme de 2 Rand () ne dépasse jamais la valeur de Rand_MAX. Une approche possible pourrait être sum = Rand ()/2 + Rand ()/2; Cela garantirait que pour un compilateur 16 bits avec une valeur Rand_MAX de 32767 même si les deux Rand retournent 32767, même dans ce cas (32767/2 = 16383) 16383 + 16383 = 32766, la somme ne serait donc pas négative.

2
Jibin Mathew

Pour éviter 0, essayez ceci:

int rnumb = Rand()%(INT_MAX-1)+1;

Vous devez inclure limits.h.

1
Doni

la raison pour laquelle j'étais en train d'ajouter était d'éviter "0" comme nombre aléatoire dans mon code. Rand () + Rand () était la solution rapide et sale qui m'est venue à l’esprit.

Une solution simple (d'accord, appelez-la un "piratage") qui ne produit jamais un résultat nul et ne débordera jamais est la suivante:

x=(Rand()/2)+1    // using divide  -or-
x=(Rand()>>1)+1   // using shift which may be faster
                  // compiler optimization may use shift in both cases

Cela limitera votre valeur maximale, mais si cela ne vous intéresse pas, cela devrait fonctionner correctement pour vous.

1
Kevin Fegan