Cours 8.8. Nombres aléatoires en C

Bibliothèques standard du C

La majorité des compilateurs C viennent avec une collection de bibliothèques normalisées standard contenant les opérations courantes (affichage, calculs mathématiques, lecture et écriture dans des fichiers ...). Ces bibliothèques contiennent principalement des fonctions qui sont utilisables en incluant l'entête de la bibliothèque concernée. Par exemple, pour utiliser la fonction printf(), il faut inclure la bibliothèque stdio.h :

#include <stdio.h>

stdlib.h

La bibliothèque sdtlib.h contient diverses fonctions de base (allocation mémoire, conversion de variables ...). Les fonctions qui vont nous intéresser ici sont les fonctions srand() et rand() qui permettent de générer des nombres aléatoires (ou plus exactement des nombres pseudo-aléatoires, mais nous y reviendrons).

Nous allons nous intéresser aux éléments suivants de la bibliothèque stdlib.h :

// Constante symbolique
#define RAND_MAX
// Fonctions
int rand(void);
void srand(unsigned int seed);

Nombres aléatoires

La fonction rand() génére un nombre pseudo-aléatoire compris entre 0 et RAND_MAX. RAND_MAX est une constante symbolique (ou macro) définie dans stdlib.h. Cette constante contient la valeur maximale retournée par la fonction rand().

Voici un exemple élémentaire :

// Affiche la valeur max du générateur pseudo-aléatoire
printf ("Valeur max : %d\n", RAND_MAX);

// Tire deux nombres aléatoires
printf ("Un nombre aléatoire : %d\n", rand());
printf ("Un autre nombre aléatoire : %d\n", rand());

Si vous exécutez le code ci-dessus plusieurs fois, vous vous apercevrez qu'il tire toujours les mêmes nombres.

Nombres pseudo-aléatoires

Un processeur est une machine deterministe qui ne peut pas générer de véritables nombres aléatoires, d'ailleurs la notion générale d'aléatoire reste discutable. Les nombres générés par la fonction rand() sont des nombres pseudo-aléatoires Un algorithme pseudo-aléatoire génére une séquence de nombres qui présentent certaines propriétés du hasard, comme par exemple l'équiprobabilité. Si le programme précédent tire toujours les mêmes nombres, c'est parce que ce sont les premiers de la séquence. La fonction srand() pour seed random (graine de l'aléatoire) permet de définir la graine du générateur et ainsi modifier le point initial de la séquence. Pour éviter d'obtenir toujours les mêmes nombres aléatoires, on utilise classiquement l'heure courante comme graine du générateur :

#include <stdlib.h>
#include <time.h>
// Initialise le générateur pseudo-aléatoire
srand(time(NULL));

Maintenant, à chaque exécution (au moins séparée d'une seconde de la précédente) le programme affiche des nombres différents.

Tirer un nombre dans un intervalle

Il est classique de vouloir tirer des nombres dans un intervalle donné. Si l'on souhaite tirer un nombre entre 0 et max, la meilleure solution est d'utiliser le reste de la division entière (modulo %) :

// x est un nombre pseudo-aléatoire entre 0 et max inclus
int x = rand() % (max+1);

Si l'on souhaite une borne inférieure, il faut décaler le tirage en ajoutant la borne inférieur :

// x est un nombre pseudo-aléatoire entre min et max inclus
int x = min + rand() % (max + 1 - min);

Tirer un nombre réel

Il est également fréquent de devoir tirer un nombre réel. L'astuce consiste à diviser le nombre généré par RAND_MAX, et ainsi obtenir un résultat entre 0 et 1 :

// x est un nombre pseudo aléatoire compris entre 0 et 1
float x = (float)rand()/(float)(RAND_MAX);

Notons le changement de type (cast int -> float) afin de réaliser la division sur des flottants. En divisant RAND_MAX par la borne max, on peut obtenir le tirage d'un nombre réel compris entre 0 et max :

// x est un nombre pseudo aléatoire entre 0 et max
float x = (float)rand() / ((float)RAND_MAX/max);

Enfin, il suffit de décaler l'intervalle pour obtenir un nombre réel dans l'intervalle [min ; max] comme vu précédemment :

// x est un nombre pseudo aléatoire entre min et max
float x = min + (float)rand() / ((float)RAND_MAX/(max-min));

Exercices

Exercice 1

Écrire un programme qui simule le tirage de deux dés à 6 faces :

Dé 1 : 6
Dé 2 : 1

Exercice 2

Pour évaluer les étudiants, un enseignant a besoin de 24 notes aléatoires dans l'intervalle [0;20]. Écrire un programme qui génère ces 24 notes :

Note n°1 : 9.2
Note n°2 : 13.1
Note n°3 : 1.1
Note n°4 : 13.2
...
Note n°21 : 17.5
Note n°22 : 18.4
Note n°23 : 19.7
Note n°24 : 15.7

Exercice 3

Ecrire une fonction hasard(int a, int b) qui tire un nombre entier au hasard dans l'intervalle [a; b]. Tirer et afficher 100 nombres au hasard entre 8 et 12 inclus :

8 9 8 11 9 9 12 12 8 8 8 12 12 11 10 11 12 10 11 11 8 11 10 11 10 9 8 10 10 10 12 
10 8 9 8 9 10 10 8 12 10 10 9 9 8 11 10 10 10 10 8 10 8 8 11 12 11 8 11 8 12 10 11 
12 8 11 8 12 10 10 12 10 8 8 11 10 8 10 9 11 12 10 10 9 12 10 9 10 11 12 11 10 12 
9 10 9 9 10 9 12

Exercice 4

Ecrire une fonction `randFloat()`` qui tire un nombre pseudo-aléatoire entre 0 et 1. Tirer un million de nombres et afficher la moyenne. La moyenne doit se situer proche de 0.5 :

Moyenne = 0.5003

Quiz

Les librairies standard sont-elles fournies avec tous les compilateur C ?

Vérifier Bravo ! Essaie encore ...

Les bibliothèques standard du C sont-elles compatibles d'un compilateur à l'autre ?

Vérifier Bravo ! Essaie encore ...

Que fait un générateur pseudo-aléatoire ?

Vérifier Bravo ! Essaie encore ...

À quoi sert cette ligne de code ?

srand(time(NULL));
Vérifier Bravo ! Essaie encore ...

Que fait le code suivant ?

x = 5 + rand()%10;
Vérifier Bravo ! Essaie encore ...

Voir aussi


Dernière mise à jour : 11/01/2023