Un générateur congruentiel linéaire est un générateur de nombres pseudo-aléatoires dont l'algorithme, introduit en 1948 par D.H Lehmer, sous une forme réduite, pour produire des nombres aléatoires, est basé sur des congruences et une fonction linéaire.
Les nombres aléatoires forment une suite dont chaque terme dépend du précédent, selon la formule suivante :
Où a est le multiplicateur, c l’incrément, m le module.
Le terme initial,
Du fait de l'opération mod (reste dans la division euclidienne de a*Xn+c par m), les termes de cette suite sont compris entre 0 et m - 1. De plus, comme chaque terme dépend entièrement du précédent, si un nombre apparaît une deuxième fois, toute la suite se reproduit à partir de ce nombre. Or le nombre de valeurs que le nombre peut prendre étant fini (égal à m), la suite est amenée a se répéter au bout d'un certain temps. On dit qu'elle est ultimement périodique.
Dans ce type d'algorithme, la qualité du générateur va entièrement dépendre du choix de a, c et m car on ne peut pas se satisfaire d'un générateur qui produit des suites plus ou moins aléatoires, sans aucune raison apparente, selon le choix de X0.
Choisir a, c et m " au hasard " n'est pas une bonne idée, prenons un exemple :
Soit le générateur congruentiel linéaire (a=25, c=16, m=256), nous obtenons
Il est clair que ces suites ne peuvent être considérées comme aléatoires.
On voit donc bien que l'on doit choisir avec précaution les paramètres du générateur si l'on espère obtenir des nombres qui s'approchent de l'aléa parfait.
Il faut alors se demander comment choisir a, c et m convenablement.
Les générateurs congruentiels font intervenir un calcul modulo m, et donc a priori une division euclidienne, ce qui peut avoir un coût de calcul important dans le cadre d'une utilisation fréquente du générateur. La solution la plus simple est d'utiliser un module de type m = 2e. En effet, les ordinateurs calculant naturellement en base binaire, un tel choix est tout à fait transparent pour eux, ce qui rend inutile une division euclidienne.
Cependant, un tel choix présente une limite importante: les bits dits de poids faible (les bits les plus à droite) sont beaucoup moins aléatoires que ceux de la partie de poids fort (les bits les plus à gauche). En effet, si d est un diviseur de m, alors la suite Yn, telle que :
satisfait à la suite congruentielle linéaire :
en particulier, avec d = 2k, pour k fixé, compris entre 1 et e, on voit que les k chiffres de poids faible, ont une période maximale de 2k, évidemment inférieure à m.
Pour remédier à ce problème, on peut ne garder que les bits de poids fort, c'est-à-dire tronquer les bits les plus à gauche du nombre obtenu. Si l'on tronque les k derniers bits, on aura alors un générateur pseudo aléatoire de nombres compris entre 0 et 2e − k − 1.
Une autre solution consiste à prendre un module du type
Afin de pouvoir choisir une graine X0 sans contraintes entre 0 et m-1, il faut chercher à maximiser la période du générateur. Or il se trouve que les valeurs de a et c sont connues, ce qui permet d'obtenir une période maximale (égale à m).
La période d’un générateur congruentiel linéaire est maximale si et seulement si :
On remarquera que l'on possède un théorème d'équivalence (si et seulement si) : la période est maximale pour toutes les valeurs qui possèdent les propriétés du théorème et seulement pour celles-là.
Le potentiel est une notion qui permet d'écarter certains générateurs insuffisamment aléatoires. Il est toujours défini que pour une suite possédant une période maximale (et ce, d'après la deuxième propriété énoncée plus haut). On ne peut pas toujours le définir sur les autres générateurs, et il existe de bons générateurs sur lesquels le potentiel n'est pas défini.
Le potentiel s d’une suite possédant une période maximale est défini comme le plus petit entier tel que :
Pour une suite de période maximale, on peut prendre X0 = 0, et donc :
ainsi, avec an = ( (a-1) + 1 )n :
et un potentiel inférieur ou égal à trois, apparaît immédiatement comme trop faible car la suite n’est pas suffisamment aléatoire. En fait un potentiel de 5 ou plus est conseillé.
Rappelons cependant que la notion de potentiel est uniquement là pour écarter quelques mauvais générateurs. Un générateur possédant un potentiel supérieur à 5 ne peut en aucun cas être d’emblée considéré comme bon, il doit d’abord subir quelques tests.
Comme tout les générateurs de nombres pseudo-aléatoires, le générateur congruentiel linéaire doit être soumis à une série de tests avant d’être homologué.
Le plus connu d’entre eux, le test de fréquence n'est généralement pas un problème pour un générateur congruentiel linéaire de période maximale, mais il existe bien d'autres tests. Test des séries, Gap test, Run test, ...
L'un des plus discriminant, car aucun générateur congruentiel linéaire démontré mauvais ne l’a réussi, étant le test spectral.
Algorithme | a | c | m | Remarques |
---|---|---|---|---|
RANDU | 65539 | 0 | 231 | Biaisé et fortement déconseillé |
générateur de Robert Sedgewick | 31415821 | 1 | 108 | Intéressant mais déconseillé (essayer avec
![]() |
Standard minimal | 16807 | 0 | 231-1 |
xn+1 = 65539 * xn mod 2^31.
Implanté sur les IBM System/370 et réputé mauvais par tous ceux qui ont à l'utiliser sérieusement
xn+1 =(129 * xn + 907633385) mod 2^32
On sait dans 1 cas sur 129 que xn+1 > xn , ce qui est un biais énorme pour une application scientifique. En plus comme 129 est de la forme 2^n + 1, les nombres produits ne sont pas si " aléatoires que ça " [cf. Knuth p.23].
xn+1 = (1103515245 * xn + 12345) mod 2^31.
Même la documentation du Berkeley 4.2 admet que les bits de poids faible des nombres produits ne sont pas vraiment aléatoires.
D. Knuth recense dans " Seminumerical Algorithms " un certain nombre de générateurs congruentiels linéaires de qualité. Si il n'est pas possible d'utiliser le Standard Minimal, on pourra essayer par exemple :
xn+1 = ( 137 * xn + 187) mod 2^8
(une suite cyclique de 256 nombres, est-ce encore du hasard ?)
xn+1 = ( 1664525 * xn + 1013904223) mod 2^32
(générateur de Knuth & Lewis)
xn+1 = 69069 * xn mod 2^32
(générateur de Marsaglia au multiplicande remarquable, utilisé sur le VAX)
xn+1 = ( 31167285 * xn + 1) mod 2^48
(générateur de Lavaux & Jenssens)
xn+1 = ( 6 364 136 223 846 793 005 * xn + 1) mod 2^64
(générateur de Haynes)
Dans tous ces cas, comme le modulant est une puissance de 2, les bits de poids faible des nombres produits ne sont pas eux très " aléatoires ". Il est par conséquent préférable de ne conserver que les poids forts (16 bits par exemple), comme le fait le générateur de Borland C++ par exemple :
xn+1 = (22 695 477 * xn + 1) mod 2^32
et on sort xn+1 >> 16.
Ce faisant, la période du générateur reste la même mais on ne va produire que des nombres compris entre 0 et 65535, chacun se retrouvant 65536 fois dans la suite.