2011-03-13 21 views
7

Estoy tratando de implementar un generador de números aleatorios distribuidos de Gauss en el intervalo [0,1].Generador de números aleatorios de Gauss

float rand_gauss (void) { 
    float v1,v2,s; 

    do { 
    v1 = 2.0 * ((float) rand()/RAND_MAX) - 1; 
    v2 = 2.0 * ((float) rand()/RAND_MAX) - 1; 

    s = v1*v1 + v2*v2; 
    } while (s >= 1.0); 

    if (s == 0.0) 
    return 0.0; 
    else 
    return (v1*sqrt(-2.0 * log(s)/s)); 
} 

Es más o menos una aplicación recta hacia adelante del algoritmo en segundo volumen de TAOCP tercera página de edición de Knuth 122.

El problema es que rand_gauss() veces devuelve valores fuera del intervalo [0, 1].

+11

Un gaussiano no tiene límites. ¿Me estoy perdiendo de algo? –

+0

hay una varianza y una media, tomo la media como 0 y la varianza^2 como 1, distribución normal estándar que es. –

+8

@nvm: una distribución normal estándar puede tomar cualquier valor entre -infinito e infinito con alguna probabilidad; no hay límite de rango en el resultado. –

Respuesta

8

Knuth describe el método polar en p 122 del 2 ° volumen de TAOCP. Ese algoritmo genera una distribución normal con mean = 0 y desviación estándar = 1. Pero puede ajustar eso multiplicando por la desviación estándar deseada y agregando la media deseada.

Puede que le resulte divertido comparar su código con otra implementación de the polar method in the C-FAQ.

4

Cambie su estado de cuenta if al (s >= 1.0 || s == 0.0). Mejor aún, use un break como se ve en el siguiente ejemplo para un número aleatorio gaussiano SIMD que genera la devolución de un par complejo (u, v). Esto utiliza el generador de números aleatorios twister Mersennedsfmt(). Si solo desea un número aleatorio único, real, devuelva solo u y guarde el v para la siguiente pasada.

inline static void randn(double *u, double *v) 
{ 
double s, x, y; // SIMD Marsaglia polar version for complex u and v 
while (1){ 
    x = dsfmt_genrand_close_open(&dsfmt) - 1.; 
    y = dsfmt_genrand_close_open(&dsfmt) - 1.; 
    s = x*x + y*y; 
    if (s < 1) break; 
} 
s = sqrt(-2.0*log(s)/s); 
*u = x*s; *v = y*s; 
return; 
} 

Este algoritmo es sorprendentemente rápido. Los tiempos de ejecución para el cálculo de dos números al azar (u, v) durante cuatro generadores de números aleatorios gaussianos diferentes son:

Times for delivering two Gaussian numbers (u + iv) 
i7-2600K @ 4GHz, gcc -Wall -Ofast -msse2 .. 

gsl_ziggurat      =  20.3 (ns) 
Box-Muller       =  78.8 (ns) 
Box-Muller with fast_sin fast_cos =  28.1 (ns) 
SIMD Marsaglia polar    =  35.0 (ns) 

El fast_sin y fast_cos rutinas polinómicas de Charles K. Garrett acelerar el cálculo de Box-Muller por un factor de 2,9 utilizando una implementación polinómica anidada de cos() y sin(). SIMD Box Muller y los algoritmos polares son ciertamente competitivos. También se pueden paralelizar fácilmente. Usando gcc -Ofast -S, el volcado de código ensamblador muestra que la raíz cuadrada es SIMD SSE2: sqrt -> sqrtsd% xmm0,% xmm0

Comentario: es realmente difícil y frustrante obtener sincronizaciones precisas con gcc5, pero creo que estos son bien: a partir del 02/03/2016: DLW

[1] enlace relacionado: c malloc array pointer return in cython

[2] Una comparación de algoritmos, pero no necesariamente para las versiones SIMD: http://www.doc.ic.ac.uk/~wl/papers/07/csur07dt.pdf

[3] Charles K. Garrett: http://krisgarrett.net/papers/l2approx.pdf

+0

yup, gracias, cambiado. 73 – W9DKI

+1

aquí está lo difícil de conseguir: gcc -Wall -Ofast -msse2 -frename-registers -malign-double -fno-strict-aliasing -DHAVE_SSE2 = 1 -DDSFMT_MEXP = 19937 -o randn_test dSFMT.c randn_test.c - lm - – W9DKI

+1

Supongo que @ W9DKI, ha creado una cuenta de engaño por error. Cuando inicie sesión la próxima vez, inicie sesión de la misma manera que lo hizo de la primera manera y no de otra manera. También marca para la fusión de cuentas. Consulte este http://stackoverflow.com/help/merging-accounts –

Cuestiones relacionadas