Question

Le code JavaScript Math.random La fonction () renvoie une valeur aléatoire comprise entre 0 et 1, générée automatiquement à l’heure actuelle (similaire à Java, je crois). Cependant, je ne pense pas qu'il y ait un moyen de vous créer votre propre graine.

Comment créer un générateur de nombres aléatoires pour lequel je puisse fournir ma propre valeur de départ, afin que je puisse le faire produire une séquence répétable de nombres (pseudo) aléatoires?

Était-ce utile?

La solution

Une option est http://davidbau.com/seedrandom , qui est une graine Math.random à base de RC4 () remplacement immédiat avec de belles propriétés.

Autres conseils

si vous n'avez pas besoin de la capacité de séparation, utilisez simplement Math.random () et créez des fonctions d'assistance autour de celle-ci (par exemple, randRange (début, fin) ). .

Je ne suis pas sûr du RNG que vous utilisez, mais il est préférable de le connaître et de le documenter afin de connaître ses caractéristiques et ses limites.

Comme Starkii l’a dit, Mersenne Twister est un bon PRNG, mais il n’est pas facile à mettre en œuvre. Si vous voulez le faire vous-même, essayez d'implémenter un LCG - c'est très facile, avec des qualités aléatoires décentes (pas aussi bon que Mersenne Twister), et vous pouvez utiliser certaines des constantes populaires.

function RNG(seed) {
  // LCG using GCC's constants
  this.m = 0x80000000; // 2**31;
  this.a = 1103515245;
  this.c = 12345;

  this.state = seed ? seed : Math.floor(Math.random() * (this.m - 1));
}
RNG.prototype.nextInt = function() {
  this.state = (this.a * this.state + this.c) % this.m;
  return this.state;
}
RNG.prototype.nextFloat = function() {
  // returns in range [0,1]
  return this.nextInt() / (this.m - 1);
}
RNG.prototype.nextRange = function(start, end) {
  // returns in range [start, end): including start, excluding end
  // can't modulu nextInt because of weak randomness in lower bits
  var rangeSize = end - start;
  var randomUnder1 = this.nextInt() / this.m;
  return start + Math.floor(randomUnder1 * rangeSize);
}
RNG.prototype.choice = function(array) {
  return array[this.nextRange(0, array.length)];
}

var rng = new RNG(20);
for (var i = 0; i < 10; i++)
  console.log(rng.nextRange(10, 50));

var digits = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
for (var i = 0; i < 10; i++)
  console.log(rng.choice(digits));

Si vous voulez pouvoir spécifier le germe, il vous suffit de remplacer les appels à getSeconds () et à getMinutes () . Vous pouvez passer un int et utiliser la moitié de celui-ci mod 60 pour la valeur en secondes et l’autre moitié modulo 60 pour vous donner l’autre partie.

Cela étant dit, cette méthode ressemble à une poubelle. Faire la génération de nombres aléatoires appropriée est très difficile. Le problème évident avec ceci est que la graine de nombre aléatoire est basée sur les secondes et les minutes. Pour deviner la graine et recréer votre flot de nombres aléatoires, il suffit d'essayer 3600 combinaisons différentes de secondes et de minutes. Cela signifie également qu'il n'y a que 3 600 semences possibles. Ceci est corrigible, mais je me méfierais de ce RNG dès le début.

Si vous souhaitez utiliser un meilleur gestionnaire de réseau de distribution, essayez le Twister de Mersenne . C'est un RNG bien testé et assez robuste avec une orbite énorme et une excellente performance.

EDIT: Je devrais vraiment avoir raison et parler de cela comme un générateur de nombres pseudo-aléatoires ou PRNG.

  

"Toute personne qui utilise des méthodes arithmétiques pour produire des nombres aléatoires est en état de péché."

  ; nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & n; ; nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & n; ; nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & n; ; nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & n; ; nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & n; ; nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & n; & nbsp; & nbsp; & nbsp; & nbsp; --- John von Neumann

J'utilise un portage JavaScript du Twister Mersenne: https://gist.github.com/300494 Il vous permet de définir la graine manuellement. En outre, comme indiqué dans d’autres réponses, le Mersenne Twister est un très bon PRNG.

Le code que vous avez énuméré ressemble à un Lehmer RNG . Si tel est le cas, alors 2147483647 est le plus grand entier signé 32 bits signé, 2147483647 est le plus grand nombre préférentiel 32 bits et 48271 est un multiplicateur de période complète utilisé pour générer les nombres.

Si cela est vrai, vous pouvez modifier RandomNumberGenerator pour intégrer un paramètre supplémentaire seed , puis définir this.seed sur graine ; mais vous devez faire attention à vous assurer que la graine donnera une bonne distribution de nombres aléatoires (Lehmer peut être bizarre comme ça) - mais la plupart des graines iront bien.

Ce qui suit est un PRNG qui peut être alimenté par une graine personnalisée. L'appel de SeedRandom renverra une fonction de générateur aléatoire. SeedRandom peut être appelé sans argument afin d'amorcer la fonction aléatoire renvoyée avec l'heure actuelle, ou peut être appelé avec 1 ou 2 inters inters non négatifs comme arguments afin de l'amorcer avec ceux-ci. entiers. En raison de la précision de la virgule flottante, l’ensemencement avec une seule valeur ne permettra au générateur d’être initié qu’à l’un des 2 ^ 53 états différents.

La fonction de générateur aléatoire renvoyée prend 1 argument entier nommé limit , la limite doit être comprise entre 1 et 4294965886, la fonction renvoie un nombre compris entre 0 et limit-1.

function SeedRandom(state1,state2){
    var mod1=4294967087
    var mul1=65539
    var mod2=4294965887
    var mul2=65537
    if(typeof state1!="number"){
        state1=+new Date()
    }
    if(typeof state2!="number"){
        state2=state1
    }
    state1=state1%(mod1-1)+1
    state2=state2%(mod2-1)+1
    function random(limit){
        state1=(state1*mul1)%mod1
        state2=(state2*mul2)%mod2
        if(state1<limit && state2<limit && state1<mod1%limit && state2<mod2%limit){
            return random(limit)
        }
        return (state1+state2)%limit
    }
    return random
}

Exemple d'utilisation:

var generator1=SeedRandom() //Seed with current time
var randomVariable=generator1(7) //Generate one of the numbers [0,1,2,3,4,5,6]
var generator2=SeedRandom(42) //Seed with a specific seed
var fixedVariable=generator2(7) //First value of this generator will always be
                                //1 because of the specific seed.

Ce générateur présente les propriétés suivantes:

  • Il a environ 2 ^ 64 différents états internes possibles.
  • Il a une période d’environ 2 ^ 63 ans, bien plus que quiconque n’aura jamais réellement besoin d’un programme JavaScript.
  • Étant donné que les valeurs mod sont des nombres premiers, il n’ya pas de modèle simple dans la sortie, quelle que soit la limite choisie. Cela diffère de certains PRNG plus simples qui présentent des schémas assez systématiques.
  • Il supprime certains résultats pour obtenir une distribution parfaite quelle que soit la limite.
  • Il est relativement lent et tourne environ 10 000 000 fois par seconde sur ma machine.

Si vous programmez en caractères manuscrit, j'adapte l'implémentation Mersenne Twister qui a été intégrée dans la réponse de Christoph Henkelmann à ce fil sous forme de classe de manuscrit:

/**
 * copied almost directly from Mersenne Twister implementation found in https://gist.github.com/banksean/300494
 * all rights reserved to him.
 */
export class Random {
    static N = 624;
    static M = 397;
    static MATRIX_A = 0x9908b0df;
    /* constant vector a */
    static UPPER_MASK = 0x80000000;
    /* most significant w-r bits */
    static LOWER_MASK = 0x7fffffff;
    /* least significant r bits */

    mt = new Array(Random.N);
    /* the array for the state vector */
    mti = Random.N + 1;
    /* mti==N+1 means mt[N] is not initialized */

    constructor(seed:number = null) {
        if (seed == null) {
            seed = new Date().getTime();
        }

        this.init_genrand(seed);
    }

    private init_genrand(s:number) {
        this.mt[0] = s >>> 0;
        for (this.mti = 1; this.mti < Random.N; this.mti++) {
            var s = this.mt[this.mti - 1] ^ (this.mt[this.mti - 1] >>> 30);
            this.mt[this.mti] = (((((s & 0xffff0000) >>> 16) * 1812433253) << 16) + (s & 0x0000ffff) * 1812433253)
                + this.mti;
            /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
            /* In the previous versions, MSBs of the seed affect   */
            /* only MSBs of the array mt[].                        */
            /* 2002/01/09 modified by Makoto Matsumoto             */
            this.mt[this.mti] >>>= 0;
            /* for >32 bit machines */
        }
    }

    /**
     * generates a random number on [0,0xffffffff]-interval
     * @private
     */
    private _nextInt32():number {
        var y:number;
        var mag01 = new Array(0x0, Random.MATRIX_A);
        /* mag01[x] = x * MATRIX_A  for x=0,1 */

        if (this.mti >= Random.N) { /* generate N words at one time */
            var kk:number;

            if (this.mti == Random.N + 1)   /* if init_genrand() has not been called, */
                this.init_genrand(5489);
            /* a default initial seed is used */

            for (kk = 0; kk < Random.N - Random.M; kk++) {
                y = (this.mt[kk] & Random.UPPER_MASK) | (this.mt[kk + 1] & Random.LOWER_MASK);
                this.mt[kk] = this.mt[kk + Random.M] ^ (y >>> 1) ^ mag01[y & 0x1];
            }
            for (; kk < Random.N - 1; kk++) {
                y = (this.mt[kk] & Random.UPPER_MASK) | (this.mt[kk + 1] & Random.LOWER_MASK);
                this.mt[kk] = this.mt[kk + (Random.M - Random.N)] ^ (y >>> 1) ^ mag01[y & 0x1];
            }
            y = (this.mt[Random.N - 1] & Random.UPPER_MASK) | (this.mt[0] & Random.LOWER_MASK);
            this.mt[Random.N - 1] = this.mt[Random.M - 1] ^ (y >>> 1) ^ mag01[y & 0x1];

            this.mti = 0;
        }

        y = this.mt[this.mti++];

        /* Tempering */
        y ^= (y >>> 11);
        y ^= (y << 7) & 0x9d2c5680;
        y ^= (y << 15) & 0xefc60000;
        y ^= (y >>> 18);

        return y >>> 0;
    }

    /**
     * generates an int32 pseudo random number
     * @param range: an optional [from, to] range, if not specified the result will be in range [0,0xffffffff]
     * @return {number}
     */
    nextInt32(range:[number, number] = null):number {
        var result = this._nextInt32();
        if (range == null) {
            return result;
        }

        return (result % (range[1] - range[0])) + range[0];
    }

    /**
     * generates a random number on [0,0x7fffffff]-interval
     */
    nextInt31():number {
        return (this._nextInt32() >>> 1);
    }

    /**
     * generates a random number on [0,1]-real-interval
     */
    nextNumber():number {
        return this._nextInt32() * (1.0 / 4294967295.0);
    }

    /**
     * generates a random number on [0,1) with 53-bit resolution
     */
    nextNumber53():number {
        var a = this._nextInt32() >>> 5, b = this._nextInt32() >>> 6;
        return (a * 67108864.0 + b) * (1.0 / 9007199254740992.0);
    }
}

vous pouvez ensuite l'utiliser comme suit:

var random = new Random(132);
random.nextInt32(); //return a pseudo random int32 number
random.nextInt32([10,20]); //return a pseudo random int in range [10,20]
random.nextNumber(); //return a a pseudo random number in range [0,1]

recherchez d'autres méthodes dans la source.

  

Remarque: ce code a été initialement inclus dans la question ci-dessus. Afin de garder la question brève et ciblée, je l’ai déplacée vers cette réponse du wiki de la communauté.

J'ai trouvé ce code flou et il semble bien fonctionner pour obtenir un nombre aléatoire puis utiliser la valeur de départ, mais je ne sais pas trop comment fonctionne la logique (par exemple, d'où proviennent les numéros 2345678901, 48271 & 2147483647 ).

function nextRandomNumber(){
  var hi = this.seed / this.Q;
  var lo = this.seed % this.Q;
  var test = this.A * lo - this.R * hi;
  if(test > 0){
    this.seed = test;
  } else {
    this.seed = test + this.M;
  }
  return (this.seed * this.oneOverM);
}

function RandomNumberGenerator(){
  var d = new Date();
  this.seed = 2345678901 + (d.getSeconds() * 0xFFFFFF) + (d.getMinutes() * 0xFFFF);
  this.A = 48271;
  this.M = 2147483647;
  this.Q = this.M / this.A;
  this.R = this.M % this.A;
  this.oneOverM = 1.0 / this.M;
  this.next = nextRandomNumber;
  return this;
}

function createRandomNumber(Min, Max){
  var rand = new RandomNumberGenerator();
  return Math.round((Max-Min) * rand.next() + Min);
}

//Thus I can now do:
var letters = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'];
var numbers = ['1','2','3','4','5','6','7','8','9','10'];
var colors = ['red','orange','yellow','green','blue','indigo','violet'];
var first = letters[createRandomNumber(0, letters.length)];
var second = numbers[createRandomNumber(0, numbers.length)];
var third = colors[createRandomNumber(0, colors.length)];

alert("Today's show was brought to you by the letter: " + first + ", the number " + second + ", and the color " + third + "!");

/*
  If I could pass my own seed into the createRandomNumber(min, max, seed);
  function then I could reproduce a random output later if desired.
*/

OK, voici la solution sur laquelle j'ai opté.

Vous commencez par créer une valeur de départ à l'aide de l'option "newseed ()". une fonction. Ensuite, vous transmettez la valeur de départ au paramètre "srandom ()". une fonction. Enfin, le "srandom ()" La fonction retourne une valeur pseudo aléatoire comprise entre 0 et 1.

Le bit crucial est que la valeur de départ est stockée dans un tableau. S'il s'agissait simplement d'un entier ou d'un flottant, la valeur serait écrasée à chaque appel de la fonction, car les valeurs des entiers, des flottants, des chaînes, etc. sont stockées directement dans la pile, par opposition aux pointeurs comme dans le cas des tableaux et autres objets. Il est donc possible que la valeur de la graine reste persistante.

Enfin, il est possible de définir le "srandom ()" fonctionner de manière à ce qu’il s’agisse d’une méthode de la méthode "Math". objet, mais je vais laisser cela à vous de comprendre. ;)

Bonne chance!

JavaScript:

// Global variables used for the seeded random functions, below.
var seedobja = 1103515245
var seedobjc = 12345
var seedobjm = 4294967295 //0x100000000

// Creates a new seed for seeded functions such as srandom().
function newseed(seednum)
{
    return [seednum]
}

// Works like Math.random(), except you provide your own seed as the first argument.
function srandom(seedobj)
{
    seedobj[0] = (seedobj[0] * seedobja + seedobjc) % seedobjm
    return seedobj[0] / (seedobjm - 1)
}

// Store some test values in variables.
var my_seed_value = newseed(230951)
var my_random_value_1 = srandom(my_seed_value)
var my_random_value_2 = srandom(my_seed_value)
var my_random_value_3 = srandom(my_seed_value)

// Print the values to console. Replace "WScript.Echo()" with "alert()" if inside a Web browser.
WScript.Echo(my_random_value_1)
WScript.Echo(my_random_value_2)
WScript.Echo(my_random_value_3)

Lua 4 (mon environnement cible personnel):

-- Global variables used for the seeded random functions, below.
seedobja = 1103515.245
seedobjc = 12345
seedobjm = 4294967.295 --0x100000000

-- Creates a new seed for seeded functions such as srandom().
function newseed(seednum)
    return {seednum}
end

-- Works like random(), except you provide your own seed as the first argument.
function srandom(seedobj)
    seedobj[1] = mod(seedobj[1] * seedobja + seedobjc, seedobjm)
    return seedobj[1] / (seedobjm - 1)
end

-- Store some test values in variables.
my_seed_value = newseed(230951)
my_random_value_1 = srandom(my_seed_value)
my_random_value_2 = srandom(my_seed_value)
my_random_value_3 = srandom(my_seed_value)

-- Print the values to console.
print(my_random_value_1)
print(my_random_value_2)
print(my_random_value_3)
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top