Générer un nombre aléatoire compris entre en C?

J’ai vu beaucoup de questions sur SO sur ce sujet particulier mais aucune d’entre elles n’a de réponse pour moi, alors j’ai pensé poser cette question.

Je voulais générer un nombre aléatoire compris entre [-1, 1]. Comment puis-je faire ceci?

Utilisez -1+2*((float)rand())/RAND_MAX

rand() génère des entiers compris dans la plage [0,RAND_MAX] , donc ((float)rand())/RAND_MAX renvoie un nombre à virgule flottante dans [0,1] . Nous obtenons des nombres aléatoires de [-1,1] en l’ajoutant à -1 .

EDIT: (en ajoutant des parties pertinentes de la section de commentaire)

Sur les limites de cette méthode:

((float)rand())/RAND_MAX renvoie un pourcentage (une fraction de 0 à 1). Donc, puisque la plage entre -1 et 1 correspond à 2 entiers, je multiplie cette fraction par 2, puis je l’ajoute au nombre minimal souhaité, -1. Cela vous indique également la qualité de vos nombres aléatoires, car vous ne disposez que de nombres aléatoires uniques RAND_MAX .

Si tout ce que vous avez est la bibliothèque Standard C, les réponses des autres personnes sont judicieuses. Si vous disposez des fonctionnalités POSIX, envisagez d’utiliser la famille de fonctions drand48 () . En particulier:

 #define _XOPEN_SOURCE 600 /* Request non-standard functions */ #include  double f = +1.0 - 2.0 * drand48(); double g = -1.0 + 2.0 * drand48(); 

Notez que le manuel dit:

Les fonctions drand48 () et erand48 () doivent renvoyer des valeurs à virgule flottante double précision, non négatives, uniformément réparties sur l’intervalle [0.0,1.0).

Si vous avez ssortingctement besoin de [-1.0,+1.0] (par opposition à [-1.0,+1.0) ), vous êtes alors confronté à un problème très délicat concernant l’extension de la plage.

Les fonctions drand48() vous donnent considérablement plus de caractère aléatoire que l’implémentation typique de rand() . Cependant, si vous avez besoin d’aléatoire cryptographique, aucun d’entre eux n’est approprié; vous devez rechercher un «PRNG cryptographiquement fort» (PRNG = générateur de nombres pseudo-aléatoires).

Il y a quelque temps, j’avais une question similaire et je pensais qu’il serait peut-être plus efficace de générer directement la fraction. J’ai fait quelques recherches et je suis tombé sur un rand intéressant à virgule flottante rapide qui n’utilise pas de division ou de multiplication à virgule flottante, ni de conversion int-> float qui peut être fait avec une connaissance intime de la représentation interne d’un float:

 float sfrand( void ) { unsigned int a=(rand()<<16)|rand(); //we use the bottom 23 bits of the int, so one //16 bit rand() won't cut it. a=(a&0x007fffff) | 0x40000000; return( *((float*)&a) - 3.0f ); } 

La première partie génère un flottant aléatoire de [2 ^ 1,2 ^ 2), soustrayez 3 et vous avez [-1, 1). Bien sûr, cela peut paraître trop intime pour certaines applications / développeurs, mais c’est ce que je cherchais. Ce mécanisme fonctionne bien pour toute gamme qui est une puissance de 2 large.

Pour commencer, vous aurez besoin de la fonction rand() la bibliothèque C. C’est dans le fichier d’en-tête stdlib.h , vous devriez donc mettre:

 #include  

près du début de votre code. rand() générera un entier aléatoire compris entre zéro et RAND_MAX . RAND_MAX conséquent, en le divisant par RAND_MAX / 2 , vous obtiendrez un nombre compris entre zéro et 2 inclus. Soustrayez-en un et vous êtes sur la plage cible de -1 à 1.

Cependant, si vous faites simplement int n = rand() / (RAND_MAX / 2) vous constaterez que vous n’obtenez pas la réponse à laquelle vous vous attendez. Cela est dû au fait que rand() et RAND_MAX / 2 sont des entiers; l’arithmétique des entiers est donc utilisée. Pour empêcher cela, certaines personnes utilisent un casting flottant, mais je recommanderais d’éviter les lancers en multipliant par 1.0 .

Vous devez également initialiser votre générateur de nombre aléatoire à l’aide de la fonction srand() . Afin d’obtenir un résultat différent à chaque fois, il est fréquent que le générateur soit basé sur l’heure, en faisant srand(time(0)) .

Donc, globalement, nous avons:

 #include  srand(time(0); double r = 1.0 * rand() / (RAND_MAX / 2) - 1; 

Bien que la réponse acceptée soit correcte dans de nombreux cas, elle laissera de côté “tous les autres chiffres”, car elle étend de 2 la plage de valeurs déjà discrètes pour couvrir l’intervalle [-1, 1]. De la même manière, si vous aviez un générateur de nombres aléatoires pouvant générer un entier de [0, 10] et que vous vouliez générer [0, 20], une simple multiplication par 2 couvrirait la plage, sans toutefois pouvoir couvrir la plage. (cela laisserait de côté tous les nombres impairs).

Il a probablement un grain suffisamment fin pour répondre à vos besoins, mais présente cet inconvénient, qui pourrait être statistiquement significatif (et préjudiciable) dans de nombreuses applications – en particulier les simulations de monte carlo et les systèmes qui dépendent étroitement des conditions initiales.

Une méthode capable de générer un nombre à virgule flottante représentable compris entre -1 et 1 inclus doit s’appuyer sur la génération d’une séquence a1.a2 a3 a4 a5 … jusqu’à la limite de votre précision en virgule flottante, qui est le seul moyen de pour générer un float possible dans la plage. (c’est-à-dire en suivant la définition des nombres réels)

De la “bibliothèque standard C”

int rand(void) – Retourne un nombre pseudo-aléatoire de 0 à RAND_MAX

RAND_MAX – Valeur maximale renvoyée par rand() .

Alors:

rand() retournera un nombre pseudo-aléatoire de 0 à RAND_MAX

rand() / RANDMAX renverra un nombre pseudo-aléatoire compris entre 0 et 1

2*( rand() / RANDMAX ) renverra un nombre pseudo-aléatoire de 0 à 2

2*( rand() / RANDMAX ) -1 renverra un nombre pseudo-aléatoire compris entre -1 et 1

Comme d’autres l’ont déjà noté, toute tentative visant à transformer simplement la plage de la fonction ‘rand ()’ de [0, RAND_MAX] en [-1, +1] souhaité produira un générateur de nombres aléatoires qui ne peut générer qu’un ensemble discret de valeurs flottantes. -point valeurs. Pour un générateur à virgule flottante, la densité de ces valeurs peut s’avérer insuffisante dans certaines applications (si la valeur de RAND_MAX définie par l’implémentation n’est pas suffisamment grande). Si cela pose un problème, on peut augmenter la densité susmentionnée de manière exponentielle en utilisant deux appels «rand ()» ou plus au lieu d’un.

Par exemple, en combinant les résultats de deux appels consécutifs à ‘rand ()’, on peut obtenir un nombre pseudo aléatoire dans la plage [0, (RAND_MAX + 1) ^ 2 – 1]

 #define RAND_MAX2 ((RAND_MAX + 1ul) * (RAND_MAX + 1) - 1) unsigned long r2 = (unsigned long) rand() * (RAND_MAX + 1) + rand(); 

et plus tard, utilisez la même méthode pour le transformer en un nombre à virgule flottante compris dans la plage [-1, +1]

 double dr2 = r2 * 2.0 / RAND_MAX2 - 1; 

En utilisant cette méthode, on peut créer autant d’appels ‘rand ()’ que nécessaire, en gardant un œil sur le dépassement d’entier, bien sûr.

En passant, cette méthode de combinaison d’appels ‘rand ()’ consécutifs ne produit pas de générateurs de nombres pseudo-aléatoires de très haute qualité, mais elle pourrait parfaitement fonctionner à de nombreuses fins.