Pimp My Code
Give your code that special "pimp"
mt_srand , seed et séquences différentes ?
Categories: Ordre et Hasard

Je reviens ici sur le fonctionnement étonnant de mt_srand() sous php.
Dans l’article précédent, je vous parlais des générateurs de nombres pseudo-aléatoires, et du “seed” qu’on peut leur fournir comme point de départ d’une séquence reproductible.

Lors de mes tests, j’avais remarqué que pour un même seed, mt_rand() retournait des séquences différentes d’un lancement à un autre.
La doc de php qui indique (en anglais, évitons les soucis de traduction)

“Since 5.2.1 the Mersenne Twister implementation in PHP now uses a new seeding algorithm by Richard Wagner. Identical seeds no longer produce the same sequence of values they did in previous versions. This behavior is not expected to change again, but it is considered unsafe to rely upon it nonetheless.”

Me semblait aller dans le même sens. Erreur (enfin, à moitié).
(Grand merci au passage à @Cdillat pour ses échanges et tests sur le sujet.)

L’équipe PHP n’a pas voulu rendre la détermination de séquence via mt_srand() inopérante.

Ce que cette phrase veut dire, c’est que, depuis 5.2.1, les séquences générées après un mt_srand() ne seront plus les mêmes que AVANT la version 5.2.1.
Elles sont toutefois censées être reproductibles d’un run à un autre pour une version de php fixe.

Le pourquoi du comment : avant php 5.2.1, seuls 31 bits sur 32 étaient initialisés. Cela a été corrigé, et modifie les séquences.
D’autres implémentations (php < 5.2.5) peuvent également différer selon que l'on est sur un OS en 32 ou 64 bits, pour des questions de débordement d'entier. Selon les versions de php, on peut donc avoir des résultats différents, même si c'est rare et à piori ne devrait plus se produire désormais. Et pourtant... en vérifiant, j'ai des machines récentes qui donnent des séquences imprévisibles d'un lancement à l'autre alors qu'initialisées avec le même seed. Voici le code test si vous voulez vérifier chez vous:


for ($i = 0; $i <10; $i++) {
    // initialisation avec un seed arbitraire 
    mt_srand(1010);
    // séquence de 100 générations
    for ($j = 0; $j < 100; $j++) {
        $prandom = mt_rand(0, 10);
    }
    echo "Run $i : $prandom\n";
}

Sur les 10 tests successifs, on devrait avoir 10 fois la même valeur. Pas partout.

L'explication se trouve, non pas directement dans PHP, mais dans le patch de sécurité suhosin et ses directives de compilation.
Selon les versions et ses options par défaut, ce patch de sécurité inactive en effet dans certains cas cette fameuse fonction mt_srand... Voilà le coupable !

En conclusion: méditez la dernière phrase de la notice php :
"This behavior is not expected to change again, but it is considered unsafe to rely upon it nonetheless."
"Ce comportement ne devrait plus changer, mais il n'est cependant pas considéré sûr d'en dépendre".

Si votre application tourne dans un contexte maitrisé, que vous pouvez vous permettre de garder de vieilles versions de php, ou de recompiler comme vous voulez les patchs de sécurité, vous pouvez utiliser le php "qui va bien" et utiliser mt_srand pour générer des séquences reproductibles. Dans le cas contraire, vous pouvez être à la merci d'une mise à jour.

Si vous préférez avoir du code portable et plus fiable, je vous encourage fortement à utiliser une implémentation pure PHP d'un générateur de nombres pseudo aléatoire, comme la méthode (simple et rapide) par congruence linéaire détaillée dans l'article précédent, ou encore une implémentation php de l'algorithme de Mersenne-Twister.
Vous maitrisez alors parfaitement le code d'initialisation.

J'insiste une nouvelle fois sur l'inadéquation totale de ces algorithmes dès qu'on parle de session, de clé, de sécurité : un générateur pseudo-aléatoire, aussi bon soit-il, même initialisé avec time(), n'est PAS, ne sera JAMAIS un générateur de nombres aléatoires.

Crédit photo : http://www.flickr.com/photos/nickwheeleroz/

Tags:,

1 Comment to “mt_srand , seed et séquences différentes ?”

  1. Martin says:

    Bien vu ! En tant que développeur issu du langage C, j’ai l’habitude que mes logiciels se comportent de manière cohérente avec la documentation, y compris dans le déterminisme des fonctions pseudo-aléatoires lorsque je le demande ainsi.

    Heureusement, la modification de l’algorithme de mt_rand() ne doit pas toucher beaucoup de logiciels, mais il y a tout de même une incidence sur le fonctionnement qui peut être gênante, dès que la suite de nombres pseudo aléatoires doit être constante entre les mises-à-jour du PHP.

    Néanmoins, je n’aurais pas eu le réflexe d’aller chercher Suhosin avant un bon moment à déboguer mon code pour trouver un défaut de fonctionnement dans le logiciel. Car mine de rien, quand on réclame du pseudo-aléatoire déterministe, c’est qu’on le veut vraiment !

    Argh, les joies du PHP…

    Bref, tu as raison, il faut une implémentation en pur PHP indépendant de la plateforme (32/64 bits) pour assurer le bon fonctionnement d’une suite déterministe… Grrr…

    Merci !

Leave a Reply