Mathématiques JavaScript.distribution normale aléatoire (courbe en cloche gaussienne)?
Je veux savoir si la fonction JavaScript est mathématique.aléatoire est une distribution normale ou non, sinon comment puis-je obtenir des nombres qui sont une distribution normale. Je n'ai pas trouvé de réponse claire sur Internet ou un algorithme pour créer des nombres aléatoires normalement distribués, ce que je comprends. Je veux reconstruire un Schmidt-machine (Allemand physicien), la machine produit des nombres aléatoires de 0 ou 1 et ils doivent être distribués normalement afin que je puisse les dessiner dans la courbe en cloche gaussienne.
pour exemple
La fonction aléatoire produit 120 nombres (0 ou 1) et la moyenne doit être proche de 60 fois 0 et 1.
16 réponses
Tout d'abord désolé de ne pas corriger correctement la réponse. J'ai oublié que cette page est indexée et que la réponse doit être correcte et utile à la personne qui a posé la question. J'espère que certains d'entre vous liront l'explication révisée et supprimeront les scores négatifs ou fourniront des commentaires sur la façon dont cette réponse pourrait être améliorée.
Je veux savoir si la fonction JavaScript Math.aléatoire est une distribution normale ou non
Mathématiques Javascript.aléatoire est pas une Distribution Normale(courbe en cloche de Gauss). From ES 2015, 20.2.2.27 " renvoie une valeur numérique avec un signe positif, supérieur ou égal à 0 mais inférieur à 1, choisie aléatoirement ou pseudo aléatoirement avec une distribution approximativement uniforme sur cette plage, en utilisant un algorithme ou une stratégie dépendant de l'implémentation. Cette fonction ne prend pas d'arguments."Ainsi, la collection fournie lorsque n est assez élevé, nous obtiendrons une distribution approximativement uniforme. Toutes les valeurs dans l'intervalle aura une probabilité égale d'apparition(ligne droite parallèle à l'axe x, indiquant un nombre compris entre 0,0 et 1,0).
Comment puis-je obtenir des nombres qui sont une distribution normale
Il existe plusieurs façons d'obtenir une collection de nombres avec une distribution normale. Comme répondu par Maxwell Collard la transformation Box-Muller transforme la distribution uniforme en distribution normale(le code peut être trouvé dans Maxwell Collard réponse).
Une réponse à une autre réponse stackoverflow à une question {[15] } a une réponse avec une autre distribution uniforme à des algorithmes de distribution normaux. Notamment: Ziggourat, Rapport des uniformes, L'inversion de la CDF En outre, l'une des réponses dit que: dit:
L'algorithme Ziggurat est assez efficace pour cela, bien que la transformation Box-Muller soit plus facile à implémenter à partir de zéro (et pas folle lente).
Et enfin
Je veux reconstruire un Schmidt-machine (physicien allemand), la machine produit des nombres aléatoires de 0 ou 1 et ils doivent être distribués normalement afin 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 retournerait pseudo-aléatoirement avec des valeurs de probabilité approximativement égales 0 et 1.
Puisque c'est le premier résultat Google pour "js Gaussian random" dans mon expérience, je me sens obligé de donner une réponse réelle à cette requête.
La transformation Box-Muller convertit deux variates uniformes indépendantes sur (0, 1) en deux variates gaussiennes standard (moyenne 0, variance 1). Ce n'est probablement pas très performant à cause de la sqrt
, log
, et cos
appelle, mais cette méthode est supérieure aux approches du théorème de la limite centrale (sommation n variates uniformes) car elle 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 );
}
Je voulais avoir approximativement des nombres aléatoires gaussiens entre 0 et 1, et après de nombreux tests j'ai trouvé 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 prime:
function gaussianRandom(start, end) {
return Math.floor(start + gaussianRand() * (end - start + 1));
}
Distribution normale entre 0 et 1
En S'appuyant sur la réponse de Maxwell, ce code utilise la transformation Box–Muller pour vous donner une distribution normale entre 0 et 1 inclus. Il rééchantillonne simplement les valeurs s'il y a plus de 3,6 écarts-types (moins de 0,02% de chance).
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
N = 100
N = 10,000
N = 10,000,000
Distribution Normale Avec Min, Max, Inclinaison
Cette version vous permet de donner un facteur min, max et skew. Voir mes exemples d'utilisation dans le 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;
}
randn_bm(-500, 1000, 1);
randn_bm(10, 20, 0.25);
randn_bm(10, 20, 3);
Les Mathématiques Javascript.la fonction pseudo-aléatoire() 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 crois que J'ai eu ça de Knuth.
Fonction qui utilise 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
}
Vous confondez la sortie de la fonction (qui est une distribution uniforme entre 0 et 1) avec la nécessité de générer une distribution gaussienne en dessinant à plusieurs reprises des nombres aléatoires qui sont 0 ou 1 - après un grand nombre d'essais, leur somme sera approximativement normalement distribuée.
Vous pouvez utiliser la fonction Math.random()
, puis arrondir le résultat en un entier: si c'est = 0.5, return 1. Maintenant, vous avez des probabilités égales de zéro et un, et vous pouvez continuez 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 0 ou 1 de manière normalement distribuée - la distribution normale nécessite une variable continue.
Lorsque vous faites ce qui précède pour disons 120 nombres, vous obtiendrez en moyenne 60 1 et 60 0. la distribution réelle que vous obtenez sera la distribution binomiale avec une moyenne de 60 et un écart type de
stdev = sqrt(p(1-p)N) = 5.48
La probabilité de un nombre particulier k
lorsque vous avez n
échantillons avec Probabilité p
(que nous avons fixé à 0.5) est
p = n! / ((n-k)! k!) p^k (1-p)^(n-k)
Lorsque p = 0.5, vous vous retrouvez avec seulement les coefficients binomiaux-qui approchent la distribution normale pour n > 30, typiquement.
15.8.2.14 aléatoire ()
Renvoie une valeur numérique avec un signe positif, supérieur ou égal à 0 mais moins de 1, choisi aléatoirement ou pseudo aléatoirement avec distribution approximativement uniforme sur cette plage , en utilisant un algorithme ou stratégie dépendant de l'implémentation. Cette fonction ne prend pas d' 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 nombres aléatoires standard dans n'importe quel langage d'exécution de base en dehors des bibliothèques de statistiques spécialisées.
Et un exemple de ligne unique:
Math.sqrt(-2 * Math.log(Math.random()))*Math.cos((2*Math.PI) * Math.random())
Et un violon https://jsfiddle.net/rszgjqf8/
Pour ceux qui sont intéressés à générer des valeurs d'une distrubution normale, je recommande de vérifier cette implémentation de L'algorithme Ziggurat en JavaScript: https://www.npmjs.com/package/node-ziggurat
Le code 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éer un Ziggurat.fichier js puis:
var z = new Ziggurat();
z.nextGaussian();
Pour moi, cela fonctionne parfaitement et comme je l'avais lu dans Wikipedia, c'est un algorithme plus efficace que le box-Muller.
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 prendre le random-node -*.fichier js directement ce dont vous avez besoin.
Http://www.simjs.com/random.html http://www.simjs.com/download.html
C'est ma solution au problème, en utilisant la méthode Marsaglia polar. La plage dépend des paramètres que vous donnez, sans Paramètres, 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 attraper la pièce de rechange si elle est là. Peut-être pas le meilleur endroit pour cela, mais bon.
Vous devrez probablement arrondir le résultat pour obtenir ce que vous vouloir.
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;
}
Une fonction non verbeuse pour échantillonner une valeur aléatoire à partir 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.
J'ai testé plusieurs fonctions avec la bonne configuration fonctionnent tous de la même manière et bien.
Http://jsfiddle.net/p3y40gf3/29/
La limite centrale est agréable, doit être avec (n = 3 pour 6) et 12 pour 12 pour regarder comme les autres. J'ai configuré d'autres aussi à (6) ou 12 ou 1/12 comme é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 exactement
Cette variante par Joe ( https://stackoverflow.com/a/33567961/466363 ) est-ce que l'écart-type est correct:
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
}
Ziggurat est également agréable mais doit être ajusté de z score à de 0 à 1 on dirait qu'il fait de bons nombres.
Box / Muller clipped est bon mais donne peu de nombres répétés aux bords coupés mais il est très similaire aux autres, les nombres aléatoires incorrects doivent être jetés pas 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
}
Variante de limite centrale il est appelé distribution Bates qui est moyen 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
let iset = 0;
let gset;
function randn() {
let v1, v2, fac, rsq;
if (iset == 0) {
do {
v1 = 2.0*Math.random() - 1.0;
v2 = 2.0*Math.random() - 1.0;
rsq = v1*v1+v2*v2;
} while ((rsq >= 1.0) || (rsq == 0));
fac = Math.sqrt(-2.0*Math.log(rsq)/rsq);
gset = v1*fac;
iset = 1;
return v2*fac;
} else {
iset = 0;
return gset;
}
}
//This is what I use for a Normal-ish distribution random function.
function normal_random(){
var pos = [ Math.random(), Math.random() ];
while ( Math.sin( pos[0] * Math.PI ) > pos[1] ){
pos = [ Math.random(), Math.random() ];
}
return pos[0];
};
Cette fonction renvoie une valeur comprise entre 0 et 1. Les valeurs proches de 0.5 sont renvoyées le plus souvent.