2008-09-16 47 views
85

¿Cómo puedo convertir una distribución uniforme (como la mayoría de los generadores de números aleatorios producen, por ejemplo, entre 0.0 y 1.0) en una distribución normal? ¿Qué pasa si quiero una desviación estándar y media de mi elección?Conversión de una distribución uniforme en una distribución normal

+3

¿Tiene una especificación de idioma o es solo una cuestión de algoritmo general? –

+2

Pregunta de algoritmo general. No me importa qué idioma. Pero preferiría que la respuesta no dependa de la funcionalidad específica que solo proporciona ese lenguaje. – Terhorst

Respuesta

44

La Ziggurat algorithm es bastante eficiente para esto, aunque la Box-Muller transform es más fácil de implementar desde cero (y no es una locura lenta).

+7

Las advertencias habituales sobre generadores congruentes lineales se aplican a ambos métodos, por lo tanto, utilice un generador subordinado decente. Aclamaciones. – dmckee

+3

Como Mersenee Twister, o tiene otras sugerencias? –

1

Yo usaría Box-Muller. Dos cosas acerca de este:

  1. Se termina con dos valores por iteración
    Por lo general, se hace una caché un valor y vuelve la otra. En la siguiente llamada para una muestra, devuelve el valor almacenado en caché.
  2. Box-Muller da un puntaje Z
    Tiene que escalar el puntaje Z por la desviación estándar y agregar el promedio para obtener el valor completo en la distribución normal.
+0

¿Cómo se escala el Z-score? – Terhorst

+2

escalado = mean + stdDev * zScore // le da normal (medio, stdDev^2) – yoyoyoyosef

1

El módulo de la biblioteca estándar de Python aleatoria tiene lo que quiere:

normalvariate (mu, sigma)
distribución Normal. mu es la media, y sigma es la desviación estándar.

Para el algoritmo mismo, eche un vistazo a la función en random.py en la biblioteca de Python.

El manual entry is here

+1

Desafortunadamente, la biblioteca de Python usa Kinderman, A.J. y Monahan, J.F., "Generación por computadora de variables aleatorias usando la relación de desviaciones uniformes", ACM Trans Math Software, 3, (1977), pp257-260. Esto utiliza dos variables aleatorias uniformes para generar el valor normal, en lugar de uno solo, por lo que no es obvio cómo usarlo como la asignación que el OP quería. – Ian

-1
function distRandom(){ 
    do{ 
    x=random(DISTRIBUTION_DOMAIN); 
    }while(random(DISTRIBUTION_RANGE)>=distributionFunction(x)); 
    return x; 
} 
+0

No se garantiza que regrese, ¿verdad? ;-) –

+0

vuelve casi seguro. –

+4

Los números aleatorios son demasiado importantes para dejarlos al azar. –

23

Cambio de la distribución de cualquier función a otra implica el uso de la inversa de la función que desea.

En otras palabras, si apunta a una función de probabilidad específica p (x) obtiene la distribución integrando sobre ella -> d (x) = integral (p (x)) y usa su inversa: Inv (d (X)). Ahora usa la función de probabilidad aleatoria (que tiene una distribución uniforme) y emite el valor del resultado a través de la función Inv (d (x)). Debería obtener valores aleatorios con distribución según la función que elija.

Este es el enfoque matemático genérico: al usarlo, ahora puede elegir cualquier función de probabilidad o distribución que tenga, siempre que tenga una aproximación inversa inversa o buena.

Espero que esto ayude y gracias por el pequeño comentario sobre el uso de la distribución y no la probabilidad en sí misma.

+4

+1 Este es un método pasado por alto para generar variables gaussianas que funciona muy bien. El CDF inverso se puede calcular de manera eficiente con el método de Newton en este caso (la derivada es e^{- t^2}), una aproximación inicial es fácil de obtener como una fracción racional, por lo que necesita 3-4 evaluaciones de erf y exp. Es obligatorio si usa números cuasialeatorios, un caso en el que debe usar exactamente un número uniforme para obtener uno gaussiano. –

+7

Tenga en cuenta que debe invertir la función de distribución acumulativa, no la función de distribución de probabilidad. Alexandre lo sugiere, pero pensé que mencionarlo de manera más explícita podría no perjudicar, ya que la respuesta parece sugerir el PDF – ltjax

+0

Puede usar el PDF si está preparado para seleccionar aleatoriamente una dirección relativa a la media; entiendo eso verdad? –

4

Use el teorema del límite central wikipedia entrymathworld entry para su ventaja.

Generar n de los números uniformemente distribuidos, sumarlos, restar n * 0.5 y tiene la salida de una distribución aproximadamente normal con media igual a 0 y varianza igual a (1/12) * (1/sqrt(N)) (ver wikipedia on uniform distributions para ese último)

n = 10 le da algo medio decente rápido.Si desea algo más que medio decente, busque la solución tylers (como se indica en el wikipedia entry on normal distributions)

+1

Esto no dará una relación particularmente cercana (las "colas" o puntos finales no se acercarán a la distribución normal real). Box-Muller es mejor, como otros han sugerido. –

+1

Box Muller tiene colas incorrectas también (devuelve un número entre -6 y 6 con doble precisión) –

+0

n = 12 (sume 12 números aleatorios en el rango de 0 a 1 y resta 6) da como resultado stddev = 1 y mean = 0 . Esto puede usarse para generar cualquier distribución normal. Simplemente multiplique el resultado por el stddev deseado y agregue el promedio. – JerryM

20

Aquí hay una implementación de javascript que usa la forma polar de la transformación Box-Muller.

/* 
* Returns member of set with a given mean and standard deviation 
* mean: mean 
* standard deviation: std_dev 
*/ 
function createMemberInNormalDistribution(mean,std_dev){ 
    return mean + (gaussRandom()*std_dev); 
} 

/* 
* Returns random number in normal distribution centering on 0. 
* ~95% of numbers returned should fall between -2 and 2 
* ie within two standard deviations 
*/ 
function gaussRandom() { 
    var u = 2*Math.random()-1; 
    var v = 2*Math.random()-1; 
    var r = u*u + v*v; 
    /*if outside interval [0,1] start over*/ 
    if(r == 0 || r >= 1) return gaussRandom(); 

    var c = Math.sqrt(-2*Math.log(r)/r); 
    return u*c; 

    /* todo: optimize this algorithm by caching (v*c) 
    * and returning next time gaussRandom() is called. 
    * left out for simplicity */ 
} 
38

Hay un montón de métodos:

  • Haz no usar la caja de Muller. Especialmente si dibujas muchos números gaussianos. Box Muller produce un resultado que se fija entre -6 y 6 (suponiendo una precisión doble. Las cosas empeoran con los flotadores). Y es realmente menos eficiente que otros métodos disponibles.
  • Zigurat está bien, pero necesita una búsqueda en la tabla (y algunos ajustes específicos de la plataforma debido a problemas de tamaño de caché)
  • Relación de los uniformes es mi favorito, sólo una adición pocos/multiplicaciones y un registro de 1/50th de el tiempo (por ejemplo, look there).
  • Invertir el CDF es eficiente (y se pasa por alto, ¿por qué?), Tiene implementaciones rápidas disponibles si busca en google. Es obligatorio para números cuasialeatorios.
+1

¿Estás seguro de la fijación [-6,6]? Este es un punto bastante significativo si es cierto (y digno de una nota en la página wikipedia). – redcalx

+1

@locster: esto es lo que un maestro mío me dijo (estudió tales generadores, y confío en su palabra). Es posible que pueda encontrar una referencia. –

+7

@locster: esta propiedad indeseable también se comparte con el método CDF inverso. Ver http://www.cimat.mx/~src/prope08/randomgauss.pdf. Esto se puede aliviar utilizando un RNG uniforme que tiene una probabilidad distinta de cero para dar un número de punto flotante muy cercano a cero. La mayoría de los RNG no lo hacen, ya que generan un entero (típicamente de 64 bits) que luego se asigna a [0,1]. Esto hace que esos métodos sean inadecuados para el muestreo de colas de las variables gaussianas (piense en la fijación de precios de las opciones bajas o altas de ataque en las finanzas computacionales). –

1

Donde R1, R2 son números aleatorios uniformes:

distribución normal, con SD de 1: sqrt (-2 * log (R1)) * cos (2 * pi * R2)

Esto es exacto ... ¡no hay necesidad de hacer todos esos bucles lentos!

+0

Antes de que alguien me corrigiera ... aquí está la aproximación que se me ocurrió: (1.5- (R1 + R2 + R3)) * 1.88. A mí también me gusta. –

0

Creo que deberías probar esto en EXCEL: =norminv(rand();0;1). Esto producirá los números aleatorios que deberían distribuirse normalmente con la media cero y la varianza unida. "0" se puede suministrar con cualquier valor, de modo que los números serán de la media deseada, y al cambiar "1", obtendrá la varianza igual al cuadrado de su entrada.

Por ejemplo: =norminv(rand();50;3) se dió a los números distribuidos normalmente con media = 50 VARIACIÓN = 9.

-3

aproximación:

function rnd_snd() { 
    return (Math.random()*2-1)+(Math.random()*2-1)+(Math.random()*2-1); 
} 

Ver http://www.protonfish.com/random.shtml

+0

Esto es simplemente incorrecto. La suma de variables aleatorias uniformes es solo 1-d [paseo aleatorio] (http://en.wikipedia.org/wiki/Random_walk) usando una distribución de entrada aleatoria uniforme. –

0

Q ¿Cómo puedo convertir una distribución uniforme (como la mayoría de los generadores de números aleatorios producen, por ejemplo, entre 0.0 y 1.0) en una distribución normal?

  1. Para la aplicación de software que sé nombres generador aleatorio par que le dan una secuencia pseudo aleatoria uniforme en [0,1] (Mersenne Twister, lineal Congruate generador). Llamémoslo U (x)

  2. Existe un área matemática que llama teoría de la probidad. Lo primero: si desea modelar r.v. con distribución integral F, entonces puede intentar simplemente evaluar F^-1 (U (x)). En pr.theory se demostró que tal r.v. tendrá una distribución integral F.

  3. El paso 2 puede ser aplicable para generar r.v. ~ F sin uso de ningún método de conteo cuando F^-1 puede obtenerse analíticamente sin problemas. (por ejemplo, exp.distribución)

  4. Para modelar la distribución normal, puede calcular y1 * cos (y2), donde y1 ~ es uniforme en [0,2pi]. y y2 es la distribución de relei.

P: ¿Qué ocurre si quiero una desviación estándar y media de mi elección?

Puede calcular sigma * N (0,1) + m.

Se puede demostrar que tal desplazamiento y plomo escalar a N (m, sigma)

1

Parece increíble que pudiera añadir algo a esto después de ocho años, pero para el caso de Java, me gustaría señalar lectores al método Random.nextGaussian(), que genera una distribución gaussiana con media 0.0 y desviación estándar 1.0 para usted.

Una simple adición y/o multiplicación cambiará la media y la desviación estándar según sus necesidades.

0

Se trata de una aplicación de Matlab usando la forma polar de la Box-Muller transformación:

Función randn_box_muller.m:

function [values] = randn_box_muller(n, mean, std_dev) 
    if nargin == 1 
     mean = 0; 
     std_dev = 1; 
    end 

    r = gaussRandomN(n); 
    values = r.*std_dev - mean; 
end 

function [values] = gaussRandomN(n) 
    [u, v, r] = gaussRandomNValid(n); 

    c = sqrt(-2*log(r)./r); 
    values = u.*c; 
end 

function [u, v, r] = gaussRandomNValid(n) 
    r = zeros(n, 1); 
    u = zeros(n, 1); 
    v = zeros(n, 1); 

    filter = r==0 | r>=1; 

    % if outside interval [0,1] start over 
    while n ~= 0 
     u(filter) = 2*rand(n, 1)-1; 
     v(filter) = 2*rand(n, 1)-1; 
     r(filter) = u(filter).*u(filter) + v(filter).*v(filter); 

     filter = r==0 | r>=1; 
     n = size(r(filter),1); 
    end 
end 

e invocando histfit(randn_box_muller(10000000),100); este es el resultado: Box-Muller Matlab Histfit

Obviamente, es muy ineficiente en comparación con el Matlab incorporado randn.

Cuestiones relacionadas