
FAQ CConsultez toutes les FAQ
Nombre d'auteurs : 28, nombre de questions : 175, création le 11 janvier 2013
Sommaire→Divers→Les nombres aléatoires
La fonction rand() renvoie des nombres pseudo-aléatoires qui sont les termes d'une suite pseudo-aléatoire. Les termes de cette suite sont des
nombres compris entre 0 et RAND_MAX.
Si cette suite est toujours la même, alors ces nombres seront aussi toujours les mêmes. Il faut donc utiliser une suite différente à chaque
exécution pour avoir des résultats assez satisfaisants. C'est là qu'intervient la fonction srand(). Cette fonction, qui permet d'initialiser le générateur de nombres aléatoires
avec le nombre passé en argument appelé graine (attention, il n'est spécifié nul part que ce nombre sera le premier terme de la suite ...),
doit toujours être appelée en début de tout programme utilisant les nombres aléatoires. Comme notre but est d'avoir une suite différente à chaque exécution,
le problème revient donc en fait à changer de graine à chaque exécution. Une méthode largement admise comme solution est d'utiliser le temps pour graine. Ainsi,
tout programme utilisant les nombres aléatoires doit toujours commencer par : 'srand((unsigned)time(NULL));'.
rand() et srand() sont déclarées dans stdlib.h. time est déclarée dans time.h.
Vous appelez certainement srand() plus d'une fois dans le programme. En effet, srand() permet d'initialiser le générateur de nombres pseudo-aléatoires. Toutefois, si l'unité de temps dans la fonction time est la seconde (ce qui est généralement le cas) et que srand(time(NULL)) est appelé plus d'une fois lors de la même seconde, le générateur est réinitialisé à chaque fois avec la même valeur et génèrera la même séquence de nombres. Ainsi :
int i;
for(i = 0; i < 10; i++)
{
srand((unsigned)time(NULL));
printf("tirage : %d\n", rand());
}
Affiche plusieurs fois le même nombre lorsqu'elle est exécutée dans la même seconde. Pour avoir un fonctionnement correct, il faut utiliser :
int i;
srand((unsigned)time(NULL));
for(i = 0; i < 10; i++)
printf("tirage : %d\n", rand());
Il suffit d'écrire une fonction linéaire qui permet de passer de l'intervalle [0, RAND_MAX] à [0, x]. Par exemple :
double rand_x(double x)
{
return rand() * (x / RAND_MAX);
}
Bien entendu, plus x est grand, plus il y aura des nombres qui ne seront jamais retournés. Dans des applications simples, x n'est jamais aussi grand (généralement 1 tout au plus !) alors que RAND_MAX vaut au minimum (cela est imposé par la norme) 32767. Si on devait vraiment générer aléatoirement un nombre x nettement grand devant 1, on pourra toujours générer dans un premier temps la partie entière puis après seulement générer la partie fractionnaire ...
La méthode la plus simple pour obtenir un nombre "aléatoire" entre 0 (inclus) et N (inclus) consiste à utiliser l'opérateur modulo : 'randvalue = rand() % (N + 1);'. Cette méthode permet, certes, d'obtenir un nombre toujours compris entre 0 et N, mais la suite de nombres obtenus n'est pas forcément très "aléatoire" car :
- Elle utilise les bits de poids faible des nombres retournés par la fonction rand() or de nombreux générateurs de nombres aléatoires simples génèrent des nombres avec des bits de poids faible non aléatoires.
- Elle introduit un biais lorsque RAND_MAX n'est pas un multiple de N + 1.
La méthode suivante permet d'éliminer les différents biais sans favoriser les bits de poids faible :
#include <stdlib.h>
int rand_n(int n)
{
int partSize = 1 + (n == RAND_MAX ? 0 : (RAND_MAX - n) / (n + 1));
int maxUsefull = partSize * n + (partSize - 1);
int draw;
do {
draw = rand();
} while (draw > maxUsefull);
return draw / partSize;
}



