Site:  Cours VB.net  
1.14 Le Hasard, l'aléatoire

        ???

Comment obtenir un nombre aléatoire?

A- Avec les instructions Rnd() et Randomize() de Visual Basic.Net

Rnd() fournit un nombre pseudo-aléatoire entre 0 et 1 (sans jamais atteindre 1); valeur inférieure à 1 mais supérieure ou égale à zéro; ce nombre pseudo-aléatoire est un Single.

En fait, si on fait des Rnd() successifs, le nombre pseudo-aléatoire précédemment généré est utilisé  pour le calcul du nombre pseudo-aléatoire suivant (avec une formule mathématique complexe), ce qui fait que la suite de nombre pseudo-aléatoire est toujours la même et qu'elle est périodique (au bout d'un grand nombre de tirage, on recommence la même suite).

Randomize() initialise le générateur de nombres pseudo-aléatoires. Si on ne donne pas d'argument, Randomize utilise la valeur de l'horloge interne pour initialiser; cette valeur est dû au hasard, aussi le Rnd qui suit va être dû au hasard.

Si on n'utilisait pas Randomize() avant Rnd(), la fonction Rnd() fournirait toujours les mêmes nombres pseudo-aléatoire dans le même ordre.

En résumé:

Rnd , si il n'y a pas d'argument, fournit une suite de nombre pseudo aléatoire (le suivant étant calculé à partir du précédent), la suite est toujours la même, seule le premier change et est initialisé par Randomize qui est basé  soit sur l'horloge système (et qui à priori initialise à une valeur toujours différente) s'il n'y a pas d'argument soit sur un argument.

Pour obtenir plusieurs fois les mêmes séries de nombres , utiliser Randomize avec un argument numérique puis appelez Rnd() avec un argument négatif.

 

Simuler un jeu de lancé de dé.

Comment obtenir un nombre entier entre un et six au hasard?

Dim MyValue As Integer

Randomize   ' Initialise le générateur de nombre aléatoire.

MyValue = CInt(Int((6 * Rnd()) + 1)) ' Génère un nombre aléatoire entre 1 et 6.

 

Rnd() fournissant un nombre aléatoire entre 0 et 1, je le multiplie par 6 et j'ajoute 1 pour qu'il soit entre 1 et 7 sans atteindre 7 (il peut être entre 1 et 6,999), je prend sa valeur entière: il est maintenant entre 1 et 6, enfin je le transforme en Integer.

On transforme Int((6 * Rnd()) + 1) en Integer car Rnd(), ne l'oublions pas, retourne un Single.

 

 

B- Avec la classe Random du Framework

Il existe une classe(faisant partie de System ) nommée Random.

La méthode Next() retourne un Integer positif entre 0 et 2 147 483 647

La méthode NextDouble() retourne un Double entre 0 et 1.

La méthode NextBytes() retourne un Byte (octet)

On peut surcharger ces méthodes pour définir des bornes.

Exemple:

J'instancie une objet à partir de la classe.

Dim Al As New Random

L'objet Al est initialisé avec une valeur probablement liée au temps, à l'horloge interne, aussi l'initialisation est 'aléatoire'.

Pour obtenir un nombre(un double) entre 0 et 1 (toujours inférieur à 1)j'écris:

MonNombrealeatoire=Al.NextDouble

Ensuite chaque NextDouble génère le nombre pseudo-aléatoire suivant (à partir d'une formule)

Noter bien que dans ce qui précède, si on fait plusieurs fois Dim Al As New Random , le nombre obtenu par NextDouble n'est jamais le même.

Par contre si on fait:

Dim Al As New Random(1)

MonNombrealeatoire=Al.NextDouble

MonNombrealeatoire=Al.NextDouble

On obtient toujours:

'0.248668584157093'

'0.110743977181029'

On obtient donc la même série car on a imposé avec Random(1) une valeur de départ qui est fonction de (1) et non du temps.

 

Pour obtenir un nombre pseudo-aléatoire entre 0 et 10, on utilise Next:

Dim Al As New Random()

MonNombrealeatoire=Al.Next(10)

MonNombrealeatoire=Al.Next(10)

On obtient la série: 2, 1, 4, 7, 6, 4, 3, 9

On travaille sur des 'Integer'

Pour obtenir un nombre pseudo-aléatoire entre 5 et 10 (mais < à 10), on utilise Next:

Dim Al As New Random()

MonNombrealeatoire=Al.Next(5,10)

MonNombrealeatoire=Al.Next(5,10)

La série comportera les nombres integers 5, 6, 7, 8, 9  (pas 10)

Pour remplir un tableau d'octets avec des octets pseudo-aléatoires, on utilise NextBytes:

Dim rnd As New Random()
Dim b(10) As Byte
rnd.NextBytes(b)

 

 

C- En cryptographie avec le Framework

Pour remplir un tableau d'octets avec des octets aléatoires forts d'un point de vue cryptographique (pour générer un mot de passe par exemple), on utilise plutôt la classe RNGCryptoServiceProvider()

L'exemple suivant crée une séquence aléatoire de 100 octets de long et la stocke dans ra.

Imports System.Security.Cryptography

Dim ra() As Byte = New Byte(100) {}
Dim rng As New RNGCryptoServiceProvider()
rng.GetBytes(ra)
' les octets dans ra sont maintenant aléatoires.
 

Il existe aussi GetNonZeroBytes pour ne pas avoir d'octet=0.

 

D - Un peu de théorie (creusons).

Un nombre aléatoire est obtenu par tirage au sort à égalité des chances, il est impossible de prévoir le tirage suivant.

Il existe des procédures physiques permettant de générer des nombres aléatoires:  comptage de désintégration par compteur Geiger, analyse de bruit..

En informatique, on utilise les nombres pseudo aléatoires générés par des algorithmes.

L'implémentation actuelle de la classe Random est basée sur l'algorithme du générateur de nombres aléatoires soustractif de Donald E. Knuth. Pour plus d'information, consultez D. E. Knuth. « The Art of Computer Programming, volume 2: Seminumerical Algorithms. » Addision-Wesley, Reading, MA, deuxième édition, 1981.

Soit un nombre de départ x (nommé' graine' ou seed en anglais)

Le nombre  est utilisé  pour le calcul du nombre pseudo-aléatoire suivant (avec une formule mathématique complexe), ce qui fait que la suite de nombre pseudo-aléatoire est toujours la même pour une même graine et qu'elle est périodique.

La formule, dite générateur à congruence linéaire, pour trouver le nombre suivant, est de la forme:

Xn+1 = (aXn+b) mod m

xn+1 = (1 664 525 xn + 1 013 904 223) mod 232 (générateur de Knuth & Lewis)
 

Voir l'excellent article sur les nombres pseudo aléatoires:Article de P larbier

et l'excellent site de D. Müller

 

On a vu que le générateur est périodique: au bout d'un certain nombre de tirage pseudo-aléatoire, dés qu'un nombre apparaît la seconde fois, on recommence la même série. En théorie, la période maximale serait m (le m de mod m dans la formule) soit 232.

Quelle est la période de la Classe Random en pratique?

Période:   81 672 063  avec Next (Integer)

Période:  562 183 903  avec NextDouble (Double)

C'est un ordre de grandeur car en fonction de la graine (valeur de départ), la série et la période sont différentes (j'ai essayé!).

Tout l'art est de choisir la graine(le premier nombre) aléatoirement!!Cela est effectué par Randomize en VB ou l'instanciation d'un objet Random dans le Framework. Randomize utilise par exemple la valeur de l'horloge interne pour initialiser; cette valeur serait dû au hasard

 

 

Amélioration :

Comment générer des nombres plus aléatoires que les pseudo aléatoires?

1- Solution créée par le programmeur:

Le nombre aléatoire est la combinaison d'un nombre pseudo aléatoire et d'un nombre probablement aléatoire par exemple:

Position de la souris

Temps entre 2 pressions sur  des touches

Statistique d'activité du disque dur.

Temps écoulé depuis.. (Ce que fait randomize).

Exemple:

Le nombre aléatoire est le produit d'un nombre pseudo aléatoire et du nombre de seconde écoulé depuis une date:

        Dim pAlea As Double    'Nombre pseudo aléatoire

        Dim second As Double   'Nombre de secondes depuis le 30/12/96

        Dim Alea  As Double    'Nombre aléatoire

        Randomize

        pAlea = Int((1000000 * Rnd) + 1)

        second = DateDiff("s", "12/30/96", Now)

        Alea =  second *pAlea

Il y a des biais, en particulier, si on utilise régulièrement cette routine, le nombre de seconde est régulièrement croissant. On pourrait améliorer en utilisant second Mod quelque chose.

2- Solution proposée par le Framework:

Méthode utilisée dans la classe System.Security.Cryptography
Le générateur
 doit être capable de produire des nombres aléatoires résistant à des attaques ou à des  analyses statistiques qui permettraient de prédire la suite.

Les méthodes courantes pour générer des nombres aléatoires en cryptographie consiste à utiliser diverses sources disponibles sur un ordinateur: temps entre deux accès au disque, taille de la mémoire, mouvements du pointeur de la souris... et à faire passer le résultat dans une fonction de hachage cryptographique comme MD5 ou SHA-1 puis à utiliser cette valeur comme graine puis...