Est-il possible de semer le générateur de nombres aléatoires (Math.random) en Javascript?
javascript
random
larmoyant
la source
la source
Réponses:
Non, ce n'est pas le cas, mais il est assez facile d'écrire votre propre générateur, ou mieux d'utiliser un générateur existant. Découvrez: cette question connexe .
Consultez également le blog de David Bau pour plus d'informations sur l'ensemencement .
la source
REMARQUE: Malgré (ou plutôt à cause de) la concision et l'élégance apparente, cet algorithme n'est en aucun cas de haute qualité en termes de caractère aléatoire. Recherchez par exemple ceux répertoriés dans cette réponse pour de meilleurs résultats.
(Adapté à l'origine d'une idée intelligente présentée dans un commentaire à une autre réponse.)
Vous pouvez définir
seed
n'importe quel nombre, évitez simplement zéro (ou tout multiple de Math.PI).L'élégance de cette solution, à mon avis, vient de l'absence de nombres "magiques" (en plus de 10000, ce qui représente environ le nombre minimum de chiffres que vous devez jeter pour éviter les motifs étranges - voir les résultats avec les valeurs 10 , 100 , 1000 ). La concision est également agréable.
C'est un peu plus lent que Math.random () (d'un facteur 2 ou 3), mais je pense que c'est à peu près aussi rapide que n'importe quelle autre solution écrite en JavaScript.
la source
J'ai implémenté un certain nombre de bonnes fonctions de générateur de nombres pseudo-aléatoires (PRNG) en JavaScript simple. Tous peuvent être ensemencés et fournir des chiffres de bonne qualité.
Tout d'abord, veillez à initialiser correctement vos PRNG. La plupart des générateurs ci-dessous n'ont pas de procédure de génération de graine intégrée (par souci de simplicité), mais acceptent une ou plusieurs valeurs 32 bits comme état initial du PRNG. Des graines similaires (par exemple une simple graine de 1 et 2) peuvent provoquer des corrélations dans des PRNG plus faibles, résultant en une sortie ayant des propriétés similaires (telles que des niveaux générés aléatoirement étant similaires). Pour éviter cela, il est préférable d'initialiser les PRNG avec une graine bien distribuée.
Heureusement, les fonctions de hachage sont très efficaces pour générer des graines pour les PRNG à partir de chaînes courtes. Une bonne fonction de hachage génèrera des résultats très différents même lorsque deux chaînes sont similaires. Voici un exemple basé sur la fonction de mixage de MurmurHash3:
Chaque appel ultérieur à la fonction de retour de
xmur3
produit une nouvelle valeur de hachage 32 bits "aléatoire" à utiliser comme valeur de départ dans un PRNG. Voici comment vous pouvez l'utiliser:Alternativement, choisissez simplement des données fictives avec lesquelles remplir la graine et avancez le générateur plusieurs fois (12-20 itérations) pour mélanger complètement l'état initial. Cela se voit souvent dans les implémentations de référence des PRNG, mais cela limite le nombre d'états initiaux.
La sortie de ces fonctions PRNG produit un nombre positif de 32 bits (0 à 2 32 -1) qui est ensuite converti en un nombre à virgule flottante entre 0-1 (0 inclus, 1 exclusif) équivalent à
Math.random()
, si vous voulez des nombres aléatoires d'une gamme spécifique, lisez cet article sur MDN . Si vous ne voulez que les bits bruts, supprimez simplement l'opération de division finale.Une autre chose à noter est les limitations de JS. Les nombres ne peuvent représenter que des entiers entiers jusqu'à une résolution de 53 bits. Et lorsque vous utilisez des opérations au niveau du bit, ce nombre est réduit à 32. Cela rend difficile l'implémentation d'algorithmes écrits en C ou C ++, qui utilisent des nombres 64 bits. Le portage de code 64 bits nécessite des shims qui peuvent réduire considérablement les performances. Donc, pour des raisons de simplicité et d'efficacité, je n'ai considéré que des algorithmes qui utilisent des mathématiques 32 bits, car il est directement compatible avec JS.
sfc32 (compteur rapide simple)
sfc32 fait partie de la suite de tests de nombres aléatoires PractRand (qu'il réussit bien sûr). sfc32 a un état de 128 bits et est très rapide dans JS.
Mulberry32
Mulberry32 est un générateur simple avec un état 32 bits, mais il est extrêmement rapide et de bonne qualité (l'auteur déclare qu'il passe tous les tests de la suite de tests gjrand et a une période complète de 2 32 , mais je n'ai pas vérifié).
Je recommanderais ceci si vous avez juste besoin d'un PRNG simple mais décent et n'avez pas besoin de milliards de nombres aléatoires (voir problème d'anniversaire ).
xoshiro128 **
Depuis mai 2018, xoshiro128 ** est le nouveau membre de la famille Xorshift , par Vigna / Blackman (qui a également écrit xoroshiro, qui est utilisé dans Chrome). C'est le générateur le plus rapide qui offre un état 128 bits.
Les auteurs affirment qu'il réussit bien les tests de hasard ( bien qu'avec des mises en garde ). D'autres chercheurs ont souligné l'échec de certains tests dans TestU01 (en particulier LinearComp et BinaryRank). En pratique, cela ne devrait pas causer de problèmes lorsque des flottants sont utilisés (tels que ces implémentations), mais peut provoquer des problèmes si vous comptez sur les bits bas bruts.
JSF (petit jeûne de Jenkins)
C'est JSF ou 'smallprng' de Bob Jenkins (2007), le gars qui a fait ISAAC et SpookyHash . Il passe les tests PractRand et devrait être assez rapide, mais pas aussi rapide que SFC.
LCG (alias Lehmer / Park-Miller RNG ou MCG)
LCG est extrêmement rapide et simple, mais la qualité de son caractère aléatoire est si faible, qu'une mauvaise utilisation peut en fait provoquer des bugs dans votre programme! Néanmoins, c'est nettement mieux que certaines réponses suggérant d'utiliser
Math.sin
ouMath.PI
! C'est un monoplace, ce qui est bien :).Cette implémentation est appelée le RNG standard minimal tel que proposé par Park – Miller en 1988 et 1993 et implémenté en C ++ 11 as
minstd_rand
. Gardez à l'esprit que l'état est 31 bits (31 bits donnent 2 milliards d'états possibles, 32 bits le double). C'est le type même de PRNG que d'autres essaient de remplacer!Cela fonctionnera, mais je ne l'utiliserais pas sauf si vous avez vraiment besoin de vitesse et que vous ne vous souciez pas de la qualité du hasard (qu'est-ce qui est aléatoire de toute façon?). Idéal pour un jeu jam ou une démo ou quelque chose. Les LCG souffrent de corrélations de graines, il est donc préférable de jeter le premier résultat d'un LCG. Et si vous insistez pour utiliser un LCG, l'ajout d'une valeur d'incrémentation peut améliorer les résultats, mais c'est probablement un exercice futile lorsque de bien meilleures options existent.
Il semble y avoir d'autres multiplicateurs offrant un état 32 bits (espace d'état accru):
Ces valeurs LCG proviennent de: P. L'Ecuyer: Tableau des générateurs linéaires congruentiels de différentes tailles et bonne structure de réseau, 30 avril 1997.
la source
seed = (seed * 185852 + 1) % 34359738337
.Math.imul
permet de déborder comme il le ferait lors de l'utilisation de la multiplication en C sur des entiers 32 bits. Ce que vous proposez est un LCG utilisant la gamme complète de l'espace entier de JS, ce qui est certainement un domaine intéressant à explorer également. :)Non, mais voici un simple générateur pseudo-aléatoire, une implémentation de Multiply-with-carry que j'ai adaptée de Wikipédia (a été supprimée depuis):
EDIT: correction de la fonction de départ en la réinitialisant m_z
EDIT2: de graves failles d'implémentation ont été corrigées
la source
seed
fonction ne réinitialise pas le générateur aléatoire, car lamz_z
variable est modifiée lors derandom()
son appel. Par conséquent, définissezmz_z = 987654321
(ou toute autre valeur) dansseed
m_w
pasm_z
. 2) Les deuxm_w
etm_z
sont modifiés en fonction de leurs valeurs précédentes, donc cela modifie le résultat.L'algorithme d'Anti Sykäri est agréable et court. J'ai initialement fait une variation qui a remplacé Math.random de Javascript lorsque vous appelez Math.seed (s), mais Jason a ensuite commenté que le retour de la fonction serait mieux:
Cela vous donne une autre fonctionnalité que Javascript n'a pas: plusieurs générateurs aléatoires indépendants. Cela est particulièrement important si vous souhaitez que plusieurs simulations répétables s'exécutent en même temps.
la source
Math.random
qui vous permettrait d'avoir plusieurs générateurs indépendants, non?Math.seed(42);
il remet à zéro la fonction, donc si vous nevar random = Math.seed(42); random(); random();
vous obtenez0.70...
, puis0.38...
. Si vous le réinitialisez en appelant àvar random = Math.seed(42);
nouveau, la prochaine fois que vous appellerez,random()
vous obtiendrez à0.70...
nouveau et la prochaine fois, vous obtiendrez à0.38...
nouveau.random
au lieu d'écraser une fonction javascript native. L'écrasementMath.random
peut entraîner le compilateur JIST à ne pas optimiser tout votre code.Veuillez voir le travail de Pierre L'Ecuyer remontant à la fin des années 1980 et au début des années 1990. Il y a d'autres aussi. Créer un générateur de nombres (pseudo) aléatoires par vous-même, si vous n'êtes pas un expert, est assez dangereux, car il y a une forte probabilité que les résultats ne soient pas statistiquement aléatoires ou qu'ils aient une petite période. Pierre (et d'autres) ont mis en place de bons (pseudo) générateurs de nombres aléatoires faciles à implémenter. J'utilise l'un de ses générateurs LFSR.
https://www.iro.umontreal.ca/~lecuyer/myftp/papers/handstat.pdf
Phil Troy
la source
En combinant certaines des réponses précédentes, voici la fonction aléatoire que vous recherchez:
la source
Math.seed(0)()
renvoie0.2322845458984375
etMath.seed(1)()
renvoie0.23228873685002327
. Changer les deuxm_w
etm_z
selon la semence semble aider.var m_w = 987654321 + s; var m_z = 123456789 - s;
produit une belle distribution des premières valeurs avec différentes graines.Écrire votre propre générateur pseudo-aléatoire est assez simple.
La suggestion de Dave Scotese est utile mais, comme l'ont souligné d'autres personnes, elle n'est pas distribuée de manière tout à fait uniforme.
Cependant, ce n'est pas à cause des arguments entiers du péché. C'est simplement à cause de l'étendue du péché, qui se trouve être une projection unidimensionnelle d'un cercle. Si vous preniez plutôt l'angle du cercle, ce serait uniforme.
Donc, au lieu de sin (x), utilisez arg (exp (i * x)) / (2 * PI).
Si vous n'aimez pas l'ordre linéaire, mélangez-le un peu avec xor. Le facteur réel n'a pas beaucoup d'importance non plus.
Pour générer n nombres pseudo aléatoires, on pourrait utiliser le code:
Veuillez également noter que vous ne pouvez pas utiliser de séquences pseudo-aléatoires lorsqu'une véritable entropie est nécessaire.
la source
Beaucoup de gens qui ont besoin d'un générateur de nombres aléatoires semable en Javascript ces jours-ci utilisent le module seedrandom de David Bau .
la source
Math.random
non, mais la RAN bibliothèque résout ce. Il a presque toutes les distributions que vous pouvez imaginer et prend en charge la génération de nombres aléatoires prédéfinis. Exemple:la source
J'ai écrit une fonction qui renvoie un nombre aléatoire prédéfini, elle utilise Math.sin pour avoir un nombre aléatoire long et utilise la graine pour choisir des nombres à partir de cela.
Utilisation :
il renverra votre nombre prédéfini le premier paramètre est une valeur de chaîne; votre graine. le deuxième paramètre est le nombre de chiffres qui reviendront.
la source
Une approche simple pour une graine fixe:
la source
Pour un nombre compris entre 0 et 100.
la source
Math.random
telle sorte que chaque fois qu'ilMath.random
est ensemencé avec la même graine, il produira la même série successive de nombres aléatoires. Cette question ne concerne pas, par exemple, l'utilisation / la démonstration réelles deMath.random
.