Pourquoi rand () + rand() produit-il des nombres négatifs?

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

for (i = 0; i < 100; i++) {
    printf("%dn", rand());
}

Mais quand j'ajoute deux appels rand(), les numéros générés ont maintenant plus de numéros négatifs.

for (i = 0; i < 100; i++) {
    printf("%d = %dn", 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 comme srand(time(NULL)).

300
demandé sur gsamaras 2015-06-13 20:15:39

8 réponses

rand() est défini pour renvoyer un nombre entier compris entre 0 et RAND_MAX.

rand() + rand()

Pourrait déborder. Ce que vous observez est probablement le résultat d'un comportement indéfini causé par un débordement d'entiers.

536
répondu P.P. 2015-06-13 17:18:10

Le problème est le plus. rand() retourne un int valeur de 0...RAND_MAX. Donc, si vous ajoutez deux d'entre eux, vous obtiendrez jusqu'à RAND_MAX * 2. Si cela dépasse INT_MAX, le résultat de l'addition dépasse la plage valide qu'un int peut contenir. Le débordement de valeurs signées est un comportement indéfini et peut conduire votre clavier à vous parler en langues étrangères.

Comme il n'y a pas de gain ici à 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 grand. Notez que long n'est pas nécessairement plus large que int, il en va de même pour long long Si int A au moins 64 bits!

Conclusion: Il suffit d'éviter l'ajout. 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 probablement un type de données plus grand que int.

Comme votre raison indiquée est d'éviter un résultat nul: cela ne peut être évité en ajoutant le résultats de deux appels rand(), car les deux peuvent être nuls. Au lieu de cela, vous pouvez simplement incrémenter. Si RAND_MAX == INT_MAX, cela ne peut pas être fait dans int. Cependant, {[16] } fera très, très probablement. Probablement (pas définitivement), car il nécessite UINT_MAX > INT_MAX, ce qui est vrai sur toutes les implémentations que je connais (qui couvre certaines architectures embarquées, DSP et toutes les plates-formes de bureau, mobiles et serveurs des 30 dernières années).

Avertissement:

Bien que déjà saupoudré dans les commentaires ici, s'il vous plait notez que l'ajout de deux valeurs aléatoires n' pas obtenir une distribution uniforme, mais une distribution triangulaire comme un lancer de deux dés: pour obtenir 12 (deux dés) les deux dés montrer 6. pour 11 il y a déjà deux variantes possibles: 6 + 5 ou 5 + 6, etc.

Donc, l'ajout est également mauvais de 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 Nombre pseudorandom générateur. Notez également que la norme ne précise pas la qualité ou la distribution uniforme des valeurs calculées.

83
répondu too honest for this site 2018-02-16 23:08:49

C'est une réponse à une clarification de la question dans le commentaire de cette réponse,

La raison pour laquelle j'ajoutais était d'éviter '0' comme nombre aléatoire dans mon code. rand () + rand() était la solution sale rapide qui m'est facilement venue à l'esprit.

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

Ce qui suit rejette zéro, évite un comportement indéfini, 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
répondu David Hammen 2017-05-23 11:47:29

Fondamentalement rand() produire des numéros 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 le débordement. Cela va bien sûr perturber la distribution des nombres aléatoires, mais rand est juste 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
répondu Khaled.K 2015-06-18 09:23:05

Peut-être que vous pourriez 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 alors (32767/2 = 16383) 16383+16383 = 32766, ainsi ne résulterait pas en somme négative.

2
répondu Jibin Mathew 2015-06-16 07:50:20

La raison pour laquelle j'ajoutais était d'éviter '0' comme nombre aléatoire dans mon code. rand () + rand() était la solution sale rapide qui m'est facilement venue à l'esprit.

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

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 vous ne vous souciez pas de cela, cela devrait fonctionner correctement pour vous.

1
répondu Kevin Fegan 2015-06-21 16:24:40

Pour éviter 0, essayez ceci:

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

Vous devez inclure limits.h.

1
répondu Doni 2018-07-15 22:39:30

Alors que ce que tout le monde a dit à propos du débordement probable pourrait très bien être la cause du négatif, même lorsque vous utilisez des entiers non signés. Le vrai problème est en fait en utilisant la fonctionnalité heure / date comme graine. Si vous vous êtes vraiment familiarisé avec cette fonctionnalité, vous saurez exactement pourquoi je dis cela. Comme ce qu'il fait vraiment est de donner une distance (temps écoulé) depuis une date/heure donnée. Bien que l'utilisation de la fonctionnalité date/heure comme graine à un rand (), est une pratique très courante, c'est vraiment pas la meilleure option. Vous devriez rechercher de meilleures alternatives, car il existe de nombreuses théories sur le sujet et je ne pouvais pas les examiner toutes. Vous ajoutez dans cette équation la possibilité de débordement et cette approche a été condamnée dès le début.

Ceux qui ont posté le rand () + 1 utilisent la solution que la plupart utilisent afin de garantir qu'ils n'obtiennent pas un nombre négatif. Mais, cette approche n'est vraiment pas la meilleure façon non plus.

La meilleure chose que vous pouvez faire est prenez le temps supplémentaire d'écrire et d'utiliser la gestion appropriée des exceptions, et ajoutez seulement au numéro rand() si et/ou lorsque vous vous retrouvez avec un résultat nul. Et, pour traiter correctement les nombres négatifs. La fonctionnalité rand() n'est pas parfaite et doit donc être utilisée conjointement avec la gestion des exceptions pour vous assurer que vous obtenez le résultat souhaité.

Prendre le temps et les efforts supplémentaires pour étudier, étudier et implémenter correctement la fonctionnalité rand() vaut bien le temps et effort. Juste mes deux cents. Bonne chance dans vos efforts...

-2
répondu Mark Krug 2015-06-15 18:34:49