mt1997 engine c++ c random distribution normal-distribution

engine - random class c++



Generar números aleatorios siguiendo una distribución normal en C/C++ (16)

Aquí hay un ejemplo de C ++, basado en algunas de las referencias. Esto es rápido y sucio, es mejor no reinventar y usar la biblioteca de impulso.

#include "math.h" // for RAND, and rand double sampleNormal() { double u = ((double) rand() / (RAND_MAX)) * 2 - 1; double v = ((double) rand() / (RAND_MAX)) * 2 - 1; double r = u * u + v * v; if (r == 0 || r > 1) return sampleNormal(); double c = sqrt(-2 * log(r) / r); return u * c; }

Puede usar un gráfico QQ para examinar los resultados y ver qué tan bien se aproxima a una distribución normal real (clasifique sus muestras 1..x, convierta los rangos en proporciones del recuento total de x, es decir, cuántas muestras, obtenga los valores z y trazarlos. Una línea recta hacia arriba es el resultado deseado).

¿Alguien sabe cómo podría generar fácilmente números aleatorios siguiendo una distribución normal en C / C ++?

http://www.mathworks.com/access/helpdesk/help/toolbox/stats/normrnd.html

No quiero usar ninguno de Boost.

Sé que Knuth habla sobre esto con detalle, pero no tengo sus libros a mano en este momento.


Así es como se generan las muestras en un compilador moderno de C ++.

#include <random> ... std::mt19937 generator; double mean = 0.0; double stddev = 1.0; std::normal_distribution<double> normal(mean, stddev); cerr << "Normal: " << normal(generator) << endl;


Creé un proyecto de código abierto en C ++ para el benchmark de generación de números aleatorios distribuidos normalmente .

Compara varios algoritmos, incluyendo

  • Método del teorema del límite central
  • Transformada Box-Muller
  • Método polar Marsaglia
  • Algoritmo Ziggurat
  • Método de muestreo de transformada inversa.
  • cpp11random usa C ++ 11 std::normal_distribution con std::minstd_rand (en realidad se trata de la transformación Box-Muller en clang).

Los resultados de la versión de precisión simple ( float ) en iMac [email protected], clang 6.1, 64 bits:

Para ser correctos, el programa verifica la media, desviación estándar, asimetría y curtosis de las muestras. Se encontró que el método CLT al sumar 4, 8 o 16 números uniformes no tiene una buena curtosis como los otros métodos.

El algoritmo Ziggurat tiene un mejor rendimiento que los demás. Sin embargo, no es adecuado para el paralelismo SIMD, ya que necesita tablas de búsqueda y ramas. Box-Muller con el conjunto de instrucciones SSE2 / AVX es mucho más rápido (x1.79, x2.99) que la versión no SIMD del algoritmo ziggurat.

Por lo tanto, sugeriré usar Box-Muller para la arquitectura con juegos de instrucciones SIMD, y puede ser ziggurat de lo contrario.

PS: el índice de referencia usa un LCG PRNG simple para generar números aleatorios distribuidos uniformes. Por lo tanto, puede no ser suficiente para algunas aplicaciones. Pero la comparación del rendimiento debe ser justa porque todas las implementaciones usan el mismo PRNG, por lo que el benchmark prueba principalmente el rendimiento de la transformación.





Implementación de Box-Muller:

#include <cstdlib> #include <cmath> #include <ctime> #include <iostream> using namespace std; // return a uniformly distributed random number double RandomGenerator() { return ( (double)(rand()) + 1. )/( (double)(RAND_MAX) + 1. ); } // return a normally distributed random number double normalRandom() { double y1=RandomGenerator(); double y2=RandomGenerator(); return cos(2*3.14*y2)*sqrt(-2.*log(y1)); } int main(){ double sigma = 82.; double Mi = 40.; for(int i=0;i<100;i++){ double x = normalRandom()*sigma+Mi; cout << " x = " << x << endl; } return 0; }


La lista de preguntas y respuestas comp.lang.c comparte tres formas diferentes de generar fácilmente números aleatorios con una distribución gaussiana.

Puede echarle un vistazo: http://c-faq.com/lib/gaussian.html


La computadora es un dispositivo determinista. No hay aleatoriedad en el cálculo. Además, el dispositivo aritmético en la CPU puede evaluar summ sobre un conjunto finito de números enteros (realizar evaluación en campo finito) y un conjunto finito de números racionales reales. Y también realizó operaciones bit a bit. Las matemáticas hacen un trato con conjuntos más grandes como [0.0, 1.0] con un número infinito de puntos.

Puedes escuchar algunos cables dentro de la computadora con algún controlador, pero ¿tendrían distribuciones uniformes? No lo sé. Pero si se asume que su señal es el resultado de acumular valores de gran cantidad de variables aleatorias independientes, entonces recibirá una variable aleatoria distribuida aproximadamente normal (se demostró en la teoría de la probabilidad)

Existen algoritmos llamados - generador pseudoaleatorio. Como pensé, el propósito del generador pseudoaleatorio es emular la aleatoriedad. Y el criterio de goodnes es: - la distribución empírica es convergente (en cierto sentido - puntual, uniforme, L2) a teórica - los valores que recibe del generador aleatorio parecen ser independientes. Por supuesto, no es cierto desde el "punto de vista real", pero suponemos que es cierto.

Uno de los métodos populares: puede sumar 12 IRV con distribuciones uniformes ... Pero para ser honesto durante la derivación del Teorema del Límite Central con la ayuda de la Transformada de Fourier, Serie Taylor, se necesita tener suposiciones n -> + inf dos veces. Entonces, por ejemplo, teóricamente - Personalmente, no entiendo cómo las personas realizan summ de 12 irv con distribución uniforme.

Tenía teoría de la capacidad en la universidad. Y particularmente para mí, es solo una pregunta matemática. En la universidad, vi el siguiente modelo:

double generateUniform(double a, double b) { return uniformGen.generateReal(a, b); } double generateRelei(double sigma) { return sigma * sqrt(-2 * log(1.0 - uniformGen.generateReal(0.0, 1.0 -kEps))); } double generateNorm(double m, double sigma) { double y2 = generateUniform(0.0, 2 * kPi); double y1 = generateRelei(1.0); double x1 = y1 * cos(y2); return sigma*x1 + m; }

De esta forma, cómo todo fue solo un ejemplo, supongo que existen otras formas de implementarlo.

La mejora que es correcta se puede encontrar en este libro "Moscú, BMSTU, 2004: XVI teoría de la probabilidad, ejemplo 6.12, p.246-247" de Krishchenko Alexander Petrovich ISBN 5-7038-2485-0

Desafortunadamente no sé sobre la existencia de la traducción de este libro al inglés.




Seguí la definición del PDF en http://www.mathworks.com/help/stats/normal-distribution.html y se me ocurrió esto:

const double DBL_EPS_COMP = 1 - DBL_EPSILON; // DBL_EPSILON is defined in <limits.h>. inline double RandU() { return DBL_EPSILON + ((double) rand()/RAND_MAX); } inline double RandN2(double mu, double sigma) { return mu + (rand()%2 ? -1.0 : 1.0)*sigma*pow(-log(DBL_EPS_COMP*RandU()), 0.5); } inline double RandN() { return RandN2(0, 1.0); }

Quizás no sea el mejor enfoque, pero es bastante simple.


Si está usando C ++ 11, puede usar http://www.cplusplus.com/reference/random/normal_distribution/ :

#include <random> std::default_random_engine generator; std::normal_distribution<double> distribution(/*mean=*/0.0, /*stddev=*/1.0); double randomNumber = distribution(generator);

Hay muchas otras distribuciones que puede usar para transformar la salida del motor de números aleatorios.


Un método rápido y fácil es solo sumar una cantidad de números aleatorios distribuidos uniformemente y tomar su promedio. Vea el Teorema del Límite Central para una explicación completa de por qué esto funciona.


Use std::tr1::normal_distribution .

El espacio de nombres std :: tr1 no forma parte de boost. Es el espacio de nombres que contiene las adiciones de la biblioteca del Informe técnico 1 de C ++ y está disponible en compiladores actualizados de Microsoft y gcc, independientemente del impulso.


EDITAR: desde el 12 de agosto de 2011 tenemos C++11 que ofrece directamente std::normal_distribution , que es la forma en que iría hoy.

Aquí está la respuesta original:

Aquí hay algunas soluciones ordenadas por complejidad ascendente.

  1. Agregue 12 números uniformes de 0 a 1 y reste 6. Esto coincidirá con la media y la desviación estándar de una variable normal. Un inconveniente obvio es que el rango está limitado a +/- 6, a diferencia de una verdadera distribución normal.

  2. Transformada de Box-Muller : se mencionó anteriormente, y es relativamente simple de implementar. Sin embargo, si necesita muestras muy precisas, tenga en cuenta que la transformada Box-Muller combinada con algunos generadores uniformes sufre de una anomalía llamada Neave Effect.

    HR Neave, "Sobre el uso de la transformación Box-Muller con generadores de números seudoaleatorios congruentes multiplicativos", Applied Statistics, 22, 92-97, 1973

  3. Para mayor precisión , sugiero dibujar uniformes y aplicar la distribución normal acumulada inversa para llegar a las variables distribuidas normalmente. Puede encontrar un algoritmo muy bueno para la distribución normal acumulada inversa en

https://web.archive.org/web/20151030215612/http://home.online.no/~pjacklam/notes/invnorm/

Espero que ayude

Peter