web-dev-qa-db-fra.com

JavaScript Math.random Distribution normale (courbe en cloche gaussienne)?

Je veux savoir si la fonction JavaScript Math.random utilise une distribution normale (vs uniforme) ou non.

Sinon, comment puis-je obtenir des nombres utilisant une distribution normale? Je n'ai pas trouvé de réponse claire sur Internet pour un algorithme permettant de créer des nombres aléatoires normalement distribués.

Je veux reconstruire une machine de Schmidt (physicien allemand). La machine produit des nombres aléatoires de 0 ou 1, et ils doivent être répartis normalement pour que je puisse les dessiner sous forme de courbe en cloche gaussienne.

Par exemple, la fonction aléatoire produit 120 nombres (0 ou 1) et la moyenne (moyenne) de ces valeurs additionnées doit être proche de 60.

43
Mangooxx

Je veux savoir si la fonction JavaScript Math.random est une distribution normale ou non

Javascript Math.random est pas une distribution normale (courbe en cloche gaussienne) . From ES 2015, 20.2.2.27 "Retourne une valeur numérique avec un signe positif, supérieure ou égale à 0 mais inférieure à 1, choisie de manière aléatoire ou pseudo-aléatoire avec une distribution approximativement uniforme dans cette plage, à l'aide d'une implémentation. algorithme ou stratégie indépendant. Cette fonction ne prend aucun argument. " Donc, la collection fournie lorsque n est suffisamment élevée, nous obtiendrons une distribution approximativement uniforme. La probabilité d'apparition de toutes les valeurs de l'intervalle sera identique (ligne droite parallèle à l'axe des x, nombre compris entre 0,0 et 1,0).

comment puis-je obtenir des nombres qui sont une distribution normale

Il y a plusieurs façons d'obtenir une collection de nombres avec une distribution normale. Comme répondu par Maxwell Collard , le transformation de Box-Muller transforme une distribution uniforme en une distribution normale (le code peut être trouvé dans réponse de Maxwell Collard ).

Une réponse à une autre réponse de type stackoverflow à une question a une réponse avec une autre distribution uniforme aux algorithmes de distribution normaux. Tels que: Ziggourat, Ratio-de-uniformes, Inverser le CDF En outre, l’une des réponses dit:

L'algorithme de Ziggurat est assez efficace pour cela, bien que la transformation de Box-Muller soit plus facile à implémenter à partir de zéro (et pas trop lente).

Et enfin

Je veux reconstruire une machine Schmidt (physicien allemand), la machine produit des nombres aléatoires de 0 ou 1 et ils doivent être distribués normalement pour que je puisse les dessiner dans la courbe en cloche gaussienne.

Lorsque nous n'avons que deux valeurs (0 ou 1), la courbe gaussienne ressemble à une distribution uniforme avec 2 valeurs possibles. C'est pourquoi un simple

function randomZero_One(){
    return Math.round(Math.random());
}

suffirait. Il reviendrait pseudo-aléatoirement avec des probabilités approximativement égales à 0 et 1.

41
kodvin

Comme c’est le premier résultat de Google pour "js gaussian random" dans mon expérience, je me sens obligé de donner une réponse réelle à cette question.

Le transformée de Box-Muller convertit deux variables identiques uniformes indépendantes sur (0, 1) en deux variables différentes gaussiennes standard (moyenne 0, variance 1). Ce n'est probablement pas très performant à cause des appels sqrt, log et cos, mais cette méthode est supérieure aux approches du théorème limite central (somme de N variables variables uniformes) car il ne limite pas la sortie à la plage limitée (-N/2, N/2). C'est aussi très simple:

// Standard Normal variate using Box-Muller transform.
function randn_bm() {
    var u = 0, v = 0;
    while(u === 0) u = Math.random(); //Converting [0,1) to (0,1)
    while(v === 0) v = Math.random();
    return Math.sqrt( -2.0 * Math.log( u ) ) * Math.cos( 2.0 * Math.PI * v );
}
97
Maxwell Collard

Distribution normale entre 0 et 1

S'appuyant sur la réponse de Maxwell, ce code utilise la transformation transformée de Box-Muller pour vous donner une distribution normale entre 0 et 1 inclus. Il rééchantillonne simplement les valeurs s'il se trouve à plus de 3,6 écarts types (moins de 0,02% de probabilité).

function randn_bm() {
    var u = 0, v = 0;
    while(u === 0) u = Math.random(); //Converting [0,1) to (0,1)
    while(v === 0) v = Math.random();
    let num = Math.sqrt( -2.0 * Math.log( u ) ) * Math.cos( 2.0 * Math.PI * v );
    num = num / 10.0 + 0.5; // Translate to 0 -> 1
    if (num > 1 || num < 0) return randn_bm(); // resample between 0 and 1
    return num;
}

Visualisations

enter image description here

n = 100

enter image description here

n = 10 000

enter image description here

n = 10 000 000

Distribution normale avec Min, Max, Skew

Cette version vous permet de donner un facteur min, max et biais. Voir mes exemples d'utilisation en bas.

function randn_bm(min, max, skew) {
    var u = 0, v = 0;
    while(u === 0) u = Math.random(); //Converting [0,1) to (0,1)
    while(v === 0) v = Math.random();
    let num = Math.sqrt( -2.0 * Math.log( u ) ) * Math.cos( 2.0 * Math.PI * v );

    num = num / 10.0 + 0.5; // Translate to 0 -> 1
    if (num > 1 || num < 0) num = randn_bm(min, max, skew); // resample between 0 and 1 if out of range
    num = Math.pow(num, skew); // Skew
    num *= max - min; // Stretch to fill range
    num += min; // offset to min
    return num;
}

enter image description here

randn_bm(-500, 1000, 1);

enter image description here

randn_bm(10, 20, 0.25);

enter image description here

randn_bm(10, 20, 3);
49
joshuakcockrell

Je voulais avoir approximativement des nombres aléatoires gaussiens compris entre 0 et 1, et après plusieurs tests je trouvais que c'était le meilleur:

function gaussianRand() {
  var Rand = 0;

  for (var i = 0; i < 6; i += 1) {
    Rand += Math.random();
  }

  return Rand / 6;
}

Et en bonus:

function gaussianRandom(start, end) {
  return Math.floor(start + gaussianRand() * (end - start + 1));
}
38
Dorian

La fonction pseudo-aléatoire Javascript Math.random () renvoie des variables qui sont également réparties entre 0 et 1. Pour obtenir une distribution gaussienne, j'utilise ceci:

// returns a gaussian random function with the given mean and stdev.
function gaussian(mean, stdev) {
    var y2;
    var use_last = false;
    return function() {
        var y1;
        if(use_last) {
           y1 = y2;
           use_last = false;
        }
        else {
            var x1, x2, w;
            do {
                 x1 = 2.0 * Math.random() - 1.0;
                 x2 = 2.0 * Math.random() - 1.0;
                 w  = x1 * x1 + x2 * x2;               
            } while( w >= 1.0);
            w = Math.sqrt((-2.0 * Math.log(w))/w);
            y1 = x1 * w;
            y2 = x2 * w;
            use_last = true;
       }

       var retval = mean + stdev * y1;
       if(retval > 0) 
           return retval;
       return -retval;
   }
}

// make a standard gaussian variable.     
var standard = gaussian(100, 15);

// make a bunch of standard variates
for(i=0; i<2000; i++) {
    console.log(standard());
}

Je pense que j'ai eu ça de Knuth.

16

Fonction utilisant le théorème de la limite centrale.

function normal(mu, sigma, nsamples){
    if(!nsamples) nsamples = 6
    if(!sigma) sigma = 1
    if(!mu) mu=0

    var run_total = 0
    for(var i=0 ; i<nsamples ; i++){
       run_total += Math.random()
    }

    return sigma*(run_total - nsamples/2)/(nsamples/2) + mu
}
12
Joe

Vous confondez le résultat de la fonction (qui est une distribution uniforme entre 0 et 1) avec la nécessité de générer une distribution gaussienne en tirant de manière répétée des nombres aléatoires qui sont 0 ou 1 - après un grand nombre d'essais, leur somme sera approximativement normalement distribué.

Vous pouvez utiliser la fonction Math.random(), puis arrondir le résultat à un entier: s'il est <0.5, renvoyer 0; si sa> = 0,5, renvoyez 1. Maintenant, vous avez des probabilités égales de zéro et une et vous pouvez continuer avec l'approche que vous avez décrite dans votre question.

Juste pour clarifier: je ne pense pas qu'il soit possible d'avoir un algorithme qui produit des 0 ou des 1 de manière distribuée normalement - la distribution normale nécessite une variable continue.

Lorsque vous effectuez ce qui précède pour environ 120 chiffres, vous obtiendrez en moyenne 60 1 et 60 0. La distribution réelle que vous obtiendrez sera la distribution binomiale avec une moyenne de 60 et un écart type de

stdev = sqrt(p(1-p)N) = 5.48

La probabilité d'un nombre particulier k lorsque vous avez n échantillons avec probabilité p (que nous avons fixée à 0,5) est

p = n! / ((n-k)! k!) p^k (1-p)^(n-k)

Lorsque p = 0,5, vous n’obtenez que les coefficients binomiaux - qui se rapprochent de la distribution normale pour n> 30, en général.

9
Floris

de la spécification:

15.8.2.14 random ()

Renvoie une valeur numérique avec un signe positif, supérieure ou égale à 0 mais inférieure à 1, choisie de manière aléatoire ou pseudo-aléatoire avec une distribution approximativement uniforme dans cette plage , utilisant un algorithme ou une stratégie dépendant de l'implémentation. Cette fonction ne prend aucun argument.

Donc, c'est une distribution uniforme , pas normale ou gaussienne. C'est ce que vous allez trouver dans à peu près n'importe quelle installation de numérotation aléatoire standard dans n'importe quel langage d'exécution de base en dehors des bibliothèques de statistiques spécialisées.

8
Pointy

Pour ceux qui sont intéressés par la génération des valeurs d'une distribution normale, je vous recommande de vérifier cette implémentation de l'algorithme Ziggurat en JavaScript: https://www.npmjs.com/package/node-ziggurat

Le code de trouvé dans la page de l'auteur est:

function Ziggurat(){

var jsr = 123456789;

var wn = Array(128);
var fn = Array(128);
var kn = Array(128);

function RNOR(){
  var hz = SHR3();
  var iz = hz & 127;
  return (Math.abs(hz) < kn[iz]) ? hz * wn[iz] : nfix(hz, iz);
}

this.nextGaussian = function(){
  return RNOR();
}

function nfix(hz, iz){
  var r = 3.442619855899;
  var r1 = 1.0 / r;
  var x;
  var y;
  while(true){
    x = hz * wn[iz];
    if( iz == 0 ){
      x = (-Math.log(UNI()) * r1); 
      y = -Math.log(UNI());
      while( y + y < x * x){
        x = (-Math.log(UNI()) * r1); 
        y = -Math.log(UNI());
      }
      return ( hz > 0 ) ? r+x : -r-x;
    }

    if( fn[iz] + UNI() * (fn[iz-1] - fn[iz]) < Math.exp(-0.5 * x * x) ){
      return x;
    }
    hz = SHR3();
    iz = hz & 127;

    if( Math.abs(hz) < kn[iz]){
      return (hz * wn[iz]);
    }
  }
}

function SHR3(){
  var jz = jsr;
  var jzr = jsr;
  jzr ^= (jzr << 13);
  jzr ^= (jzr >>> 17);
  jzr ^= (jzr << 5);
  jsr = jzr;
  return (jz+jzr) | 0;
}

function UNI(){
  return 0.5 * (1 + SHR3() / -Math.pow(2,31));
}

function zigset(){
  // seed generator based on current time
  jsr ^= new Date().getTime();

  var m1 = 2147483648.0;
  var dn = 3.442619855899;
  var tn = dn;
  var vn = 9.91256303526217e-3;

  var q = vn / Math.exp(-0.5 * dn * dn);
  kn[0] = Math.floor((dn/q)*m1);
  kn[1] = 0;

  wn[0] = q / m1;
  wn[127] = dn / m1;

  fn[0] = 1.0;
  fn[127] = Math.exp(-0.5 * dn * dn);

  for(var i = 126; i >= 1; i--){
    dn = Math.sqrt(-2.0 * Math.log( vn / dn + Math.exp( -0.5 * dn * dn)));
    kn[i+1] = Math.floor((dn/tn)*m1);
    tn = dn;
    fn[i] = Math.exp(-0.5 * dn * dn);
    wn[i] = dn / m1;
  }
}
zigset();
}

Créez un fichier Ziggurat.js puis:

var z = new Ziggurat();
z.nextGaussian();

Pour moi, cela fonctionne parfaitement et comme je l'avais lu dans Wikipedia, il s'agit d'un algorithme plus efficace que le Box-Muller.

entrez la description du lien ici

4
Eduardo G.R.

Et un exemple d'une seule ligne:

Math.sqrt(-2 * Math.log(Math.random()))*Math.cos((2*Math.PI) * Math.random())

et a Fiddle https://jsfiddle.net/rszgjqf8/

4
unsalted

Une fonction non verbeuse pour échantillonner une valeur aléatoire d'une distribution gaussienne que j'ai écrite il y a quelque temps:

function gaussianRandom(mean, sigma) {
  let u = Math.random()*0.682;
  return ((u % 1e-8 > 5e-9 ? 1 : -1) * (Math.sqrt(-Math.log(Math.max(1e-9, u)))-0.618))*1.618 * sigma + mean;
}

Cela devrait fonctionner si vous fixez les valeurs à la plage souhaitée.

1
SuperEggbert

J'ai testé plusieurs fonctions avec la bonne configuration, toutes fonctionnent de la même manière et de manière similaire.

http://jsfiddle.net/p3y40gf3/29/

La limite centrale est Nice, doit être avec (n = 3 pour 6) et 12 pour 12 pour ressembler aux autres. J'ai également configuré les autres sur (6) ou 12 ou 1/12 en tant qu'écart-type, je ne sais pas pourquoi 12.

La limite centrale est un peu moins centrée que Box/Muller et Ziggurat.

Box/Muller et Ziggurat se ressemblent

cette variante de Joe ( --- (https://stackoverflow.com/a/33567961/46636 ) effectue correctement la déviation standard:

function normal(mu, sigma, nsamples){ // using central limit
    if(!nsamples) nsamples = 3
    if(!sigma) sigma = 1
    if(!mu) mu=0

    var run_total = 0
    for(var i=0 ; i<nsamples ; i++){
       run_total += Math.random()
    }

    return sigma*(run_total - nsamples/2)/(nsamples/2) + mu
}

La ziggourat est également agréable, mais elle doit être ajustée de z à 0 à 1 et donne de bons chiffres.

Box/Muller coupé est bon mais donne peu de nombres répétés sur les bords mais il est très similaire aux autres, les nombres aléatoires incorrects doivent être mis au rebut et non coupés.

function randn_bm() {
    var u = 0, v = 0;
    while(u === 0) u = Math.random(); //Converting [0,1) to (0,1)
    while(v === 0) v = Math.random();
    let num = Math.sqrt( -2.0 * Math.log( u ) ) * Math.cos( 2.0 * Math.PI * v );
    num = num / 6.0 + 0.5; // Translate to 0 -> 1 // changed here 10 to 6
    if(num>1||num<0) return randn_bm(); return num; // bad random numbers should be discared not clipped
    //return Math.max(Math.min(num, 1), 0); // cap between 0 and 1
}

La variante de la limite centrale appelée distribution Bates est moyenne https://en.wikipedia.org/wiki/Bates_distribution

pas confondu avec Irwin Hall qui est une somme https://en.wikipedia.org/wiki/Irwin%E2%80%93Hall_distribution

https://en.wikipedia.org/wiki/Normal_distribution#Generating_values_from_normal_distribution

1
Shimon Doodkin

J'ai trouvé cette bibliothèque qui inclut beaucoup de fonctions aléatoires utiles. Vous pouvez l'installer via simjs à partir de npm ou simplement extraire directement le fichier random-node - *. Js pour ce dont vous avez besoin.

http://www.simjs.com/random.htmlhttp://www.simjs.com/download.html

1
Mark

C’est ma solution au problème, en utilisant la méthode polaire de Marsaglia . La plage dépend des paramètres que vous indiquez. Sans paramètre, elle ne génère presque jamais rien en dehors de la plage.

Comme il génère deux nombres normalement distribués par itération, j'ai déclaré une variable sous window.temp.spareNormal pour récupérer le numéro en réserve, le cas échéant. Peut-être pas le meilleur endroit pour ça, mais bon.

Vous devrez probablement arrondir le résultat pour obtenir ce que vous voulez.

window.temp = {
    spareNormal: undefined
};

Math.normal = function (mean, standardDeviation) {
    let q, u, v, p;

    mean = mean || 0.5;
    standardDeviation = standardDeviation || 0.125;

    if (typeof temp.spareNormal !== 'undefined') {
        v = mean + standardDeviation * temp.spareNormal;
        temp.spareNormal = undefined;

        return v;
    }

    do  {
        u = 2.0 * Math.random() - 1.0;
        v = 2.0 * Math.random() - 1.0;

        q = u * u + v * v;
    } while (q >= 1.0 || q === 0);

    p = Math.sqrt(-2.0 * Math.log(q) / q);

    temp.spareNormal = v * p;
    return mean + standardDeviation * u * p;
}
1
Sjor