javascript - sacar - distribucion normal estandar
JavaScript Math.random Distribución normal(curva de campana gaussiana)? (16)
Quiero saber si la función de JavaScript Math.random
usa una distribución normal (vs. uniforme) o no.
Si no, ¿cómo puedo obtener números que usen una distribución normal? No he encontrado una respuesta clara en Internet para un algoritmo para crear números aleatorios distribuidos normalmente.
Quiero reconstruir una máquina Schmidt (físico alemán). La máquina produce números aleatorios de 0 o 1, y tienen que estar distribuidos normalmente para que pueda dibujarlos como una curva de campana gaussiana.
Por ejemplo, la función aleatoria produce 120 números (0 o 1) y el promedio (promedio) de estos valores sumados tiene que estar cerca de 60.
Distribución normal entre 0 y 1
Sobre la base de la respuesta de Maxwell, este código utiliza la transformación de Box-Muller para darle una distribución normal entre 0 y 1, ambos incluidos. Simplemente vuelve a muestrear los valores si están a más de 3.6 desviaciones estándar (menos de 0.02% de probabilidad).
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;
}
Visualizaciones
n = 100
n = 10,000
n = 10,000,000
Distribución normal con mín, máx, sesgo
Esta versión le permite dar un factor mínimo, máximo y sesgo. Ver mis ejemplos de uso en la parte inferior.
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);
Confunde la salida de la función (que es una distribución uniforme entre 0 y 1) con la necesidad de generar una distribución gaussiana al dibujar repetidamente números aleatorios que son 0 o 1, después de un gran número de intentos, su suma será aproximadamente normalmente distribuido.
Puedes usar la función Math.random()
, luego redondear el resultado a un entero: si es <0.5, devuelve 0; si es> = 0.5, devuelva 1. Ahora tiene las mismas probabilidades de cero y uno, y puede continuar con el enfoque que describió en su pregunta.
Solo para aclarar: no creo que sea posible tener un algoritmo que produzca 0 o 1 en una forma normalmente distribuida; la distribución normal requiere una variable continua.
Cuando haga lo anterior para, por ejemplo, 120 números, obtendrá en promedio 60 1 y 60 0. La distribución real que obtenga será la distribución binomial con una media de 60 y una desviación estándar de
stdev = sqrt(p(1-p)N) = 5.48
La probabilidad de un número particular k
cuando tiene n
muestras con probabilidad p
(que fijamos en 0.5) es
p = n! / ((n-k)! k!) p^k (1-p)^(n-k)
Cuando p = 0.5, terminas solo con los coeficientes binomiales, que se acercan a la distribución normal para n> 30, generalmente.
Dado que este es el primer resultado de Google para "js gaussian random" en mi experiencia, siento la obligación de dar una respuesta real a esa consulta.
La transformada de Box-Muller convierte dos variables uniformes independientes en (0, 1) en dos variables gaussianas estándar (media 0, varianza 1). Probablemente esto no sea muy eficaz debido a las llamadas sqrt
, log
y cos
, pero este método es superior al enfoque del teorema del límite central (sumando N variables uniformes) porque no restringe la salida al rango delimitado (-N / 2, N / 2). También es muy 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 );
}
En primer lugar, lo siento por no corregir correctamente la respuesta. Olvidé que esta página está indexada y que la respuesta DEBE ser correcta, además de ser útil para la persona que hizo la pregunta. Espero que algunos de ustedes lean la explicación revisada y eliminen las puntuaciones negativas o proporcionen comentarios sobre cómo esta respuesta podría mejorarse aún más.
Quiero saber si la función de JavaScript Math.random es distribución normal o no
Javascript Math.random no es una distribución normal (curva de campana gaussiana). Desde ES 2015, 20.2.2.27 "Devuelve un valor numérico con signo positivo, mayor o igual que 0 pero menor que 1, elegido aleatoriamente o pseudoaleatoriamente con una distribución aproximadamente uniforme en ese rango, utilizando un algoritmo o estrategia dependiente de la implementación. La función no tiene argumentos ". Así que la colección provista cuando n es lo suficientemente alta obtendremos una distribución aproximadamente uniforme. Todos los valores en el intervalo tendrán la misma probabilidad de aparición (línea recta paralela al eje x, que denota el número entre 0.0 y 1.0).
¿Cómo puedo obtener los números que son de distribución normal?
Hay varias formas de obtener una colección de números con una distribución normal. Según la respuesta de Maxwell Collard, la transformación Box-Muller transforma la distribución uniforme en distribución normal (el código se puede encontrar en la respuesta de Maxwell Collard ).
Una respuesta a otra respuesta de a una question tiene una respuesta con otra distribución uniforme a algoritmos de distribución normal. Tales como: Ziggurat, Ratio-of-uniforms, Inverting the CDF. Además, una de las respuestas dice que:
El algoritmo Ziggurat es bastante eficiente para esto, aunque la transformación Box-Muller es más fácil de implementar desde cero (y no es una locura lenta).
Y finalmente
Quiero reconstruir una máquina Schmidt (físico alemán), la máquina produce números aleatorios de 0 o 1 y tienen que tener una distribución normal para poder dibujarlos en la curva de campana gaussiana.
Cuando tenemos solo dos valores (0 o 1), la curva gaussiana se ve igual que la distribución uniforme con 2 valores posibles. Es por eso que un simple.
function randomZero_One(){
return Math.round(Math.random());
}
bastaría. Devolvería pseudoaleatoria con valores de probabilidad aproximadamente iguales 0 y 1.
Encontré esta biblioteca que incluye muchas funciones aleatorias útiles. Puede instalarlo a través de simjs desde npm, o simplemente sacar el archivo random-node - *. Js directamente para lo que necesite.
http://www.simjs.com/random.html http://www.simjs.com/download.html
Esta es mi solución al problema, utilizando el método polar de Marsaglia . El rango depende de los parámetros que proporcione, sin parámetros casi nunca genera nada fuera del rango.
Como genera dos números distribuidos normalmente por iteración, declaré una variable en window.temp.spareNormal para tomar el repuesto si está allí. Puede que no sea el mejor lugar para ello, pero oye.
Probablemente tendrías que redondear el resultado para obtener lo que quieres.
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;
}
Función que utiliza el teorema del límite central.
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
}
He probado varias funciones con la configuración correcta, todas funcionan de manera similar y bien.
http://jsfiddle.net/p3y40gf3/29/
El límite central es bueno, debe estar con (n = 3 para 6) y 12 para que 12 se vean como los demás. Configuré otros también a (6) o 12 o 1/12 como desviación estándar, no estoy seguro de por qué 12.
El límite central es un poco menos centrado que Box / Muller y Ziggurat.
Box / Muller y Ziggurat tienen exactamente el mismo aspecto
esta variante de Joe ( https://.com/a/33567961/466363 ) hace la desviación estándar correctamente:
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;
}
Ziggurat también es bueno, pero debe ajustarse de la puntuación z a la apariencia de 0 a 1, ya que hace buenos números.
El recorte de Box / Muller es bueno, pero ofrece pocos números repetidos en los bordes recortados, pero es muy similar a otros, los números aleatorios incorrectos deben descartarse, no recortarse.
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
}
Variante de límite central, se denomina distribución de Bates que tiene un promedio de https://en.wikipedia.org/wiki/Bates_distribution
no se confunde con Irwin Hall, que es una suma https://en.wikipedia.org/wiki/Irwin%E2%80%93Hall_distribution
https://en.wikipedia.org/wiki/Normal_distribution#Generating_values_from_normal_distribution
La función pseudoaleatoria Javascript Math.random () devuelve variables que se distribuyen por igual entre 0 y 1. Para obtener una distribución gaussiana, uso esto:
// 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());
}
Creo que obtuve esto de Knuth.
Para aquellos interesados en generar valores de una distrubución normal, recomendaría verificar esta implementación del algoritmo Ziggurat en JavaScript: https://www.npmjs.com/package/node-ziggurat
El código de encontrado en la página del autor es:
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();
}
Crea un archivo Ziggurat.js y luego:
var z = new Ziggurat();
z.nextGaussian();
Para mí está funcionando perfectamente y, como había leído en Wikipedia, este es un algoritmo más eficiente que el Box-Muller.
Quería tener aproximadamente números aleatorios gaussianos entre 0 y 1, y después de muchas pruebas encontré que este era el mejor:
function gaussianRand() {
var rand = 0;
for (var i = 0; i < 6; i += 1) {
rand += Math.random();
}
return rand / 6;
}
Y como bonificación:
function gaussianRandom(start, end) {
return Math.floor(start + gaussianRand() * (end - start + 1));
}
Una función no detallada para muestrear un valor aleatorio de una distribución gaussiana que escribí hace algún tiempo:
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
}
Debería funcionar si fijas los valores al rango que deseas.
Y un ejemplo de una sola línea:
Math.sqrt(-2 * Math.log(Math.random()))*Math.cos((2*Math.PI) * Math.random())
y un Fiddle https://jsfiddle.net/rszgjqf8/
15.8.2.14 aleatorio ()
Devuelve un valor numérico con signo positivo, mayor o igual que 0 pero menor que 1, elegido aleatoriamente o pseudo aleatoriamente con una distribución aproximadamente uniforme en ese rango , utilizando un algoritmo o estrategia que depende de la implementación. Esta función no tiene argumentos.
Entonces, es una distribución uniforme , no normal o gaussiana. Eso es lo que encontrará en casi cualquier servicio estándar de números aleatorios en cualquier tiempo de ejecución de lenguaje básico fuera de las bibliotecas de estadísticas especializadas.
//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];
};
Esta función devuelve un valor entre 0 y 1. Los valores cercanos a 0.5 se devuelven con mayor frecuencia.
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;
}
}