Generar entero aleatorio de un rango

Necesito una función que generaría un entero aleatorio en un rango dado (incluidos los valores de frontera). No tengo requisitos irracionales de calidad / aleatoriedad, tengo cuatro requisitos:

  • Necesito que sea rápido. Mi proyecto necesita generar millones (o incluso decenas de millones) de números aleatorios y mi función de generador actual ha demostrado ser un cuello de botella.
  • Necesito que sea razonablemente uniforme (el uso de rand () está perfectamente bien).
  • los rangos min-max pueden ser cualquier cosa desde a .
  • tiene que ser visible

Actualmente tengo el siguiente código de C ++:

output = min + (rand() * (int)(max - min) / RAND_MAX) 

El problema es que no es realmente uniforme: max se devuelve solo cuando rand () = RAND_MAX (para Visual C ++ es 1/32727). Este es un problema importante para rangos pequeños como , donde casi nunca se devuelve el último valor.

Así que agarré lápiz y papel y se me ocurrió la siguiente fórmula (que se basa en el truco de redondeo entero (int) (n + 0.5)):

enter image description here

Pero todavía no me da una distribución uniforme. Las ejecuciones repetidas con 10000 muestras dan una proporción de 37:50:13 para valores de valores -1, 0. 1.

¿Podría sugerir una mejor fórmula? (o incluso toda la función del generador de números pseudoaleatorio)

Una solución distribuida rápida, algo mejor que la tuya, pero aún no es la adecuada

 output = min + (rand() % static_cast(max - min + 1)) 

Excepto cuando el tamaño del rango es una potencia de 2, este método produce números distribuidos no uniformes y sesgados independientemente de la calidad de rand() . Para una prueba completa de la calidad de este método, lea esto .

La respuesta más simple (y por lo tanto mejor) a C ++ (usando el estándar 2011) es

 #include  std::random_device rd; // only used once to initialise (seed) engine std::mt19937 rng(rd()); // random-number engine used (Mersenne-Twister in this case) std::uniform_int_distribution uni(min,max); // guaranteed unbiased auto random_integer = uni(rng); 

No hay necesidad de reinventar la rueda. No hay necesidad de preocuparse por el sesgo. No hay necesidad de preocuparse por usar el tiempo como semilla aleatoria.

Si su comstackdor admite C ++ 0x y usarlo es una opción para usted, entonces es probable que el nuevo encabezado estándar satisfaga sus necesidades. Tiene una uniform_int_distribution alta calidad que aceptará límites mínimos y máximos (todo lo que necesite), y puede elegir entre varios generadores de números aleatorios para conectarse a esa distribución.

Aquí hay un código que genera un millón de int aleatorios distribuidos uniformemente en [-57, 365]. He utilizado las nuevas instalaciones estándar para cronometrarlo, ya que mencionas que el rendimiento es una gran preocupación para ti.

 #include  #include  #include  int main() { typedef std::chrono::high_resolution_clock Clock; typedef std::chrono::duration sec; Clock::time_point t0 = Clock::now(); const int N = 10000000; typedef std::minstd_rand G; G g; typedef std::uniform_int_distribution<> D; D d(-57, 365); int c = 0; for (int i = 0; i < N; ++i) c += d(g); Clock::time_point t1 = Clock::now(); std::cout << N/sec(t1-t0).count() << " random numbers per second.\n"; return c; } 

Para mí (2.8 GHz Intel Core i5) esto se imprime:

2.10268e + 07 números aleatorios por segundo.

Puedes sembrar el generador pasando un int a su constructor:

  G g(seed); 

Si luego encuentra que int no cubre el rango que necesita para su distribución, esto puede remediarse cambiando la uniform_int_distribution como tal (por ejemplo, long long ):

  typedef std::uniform_int_distribution D; 

Si luego encuentra que el minstd_rand no es un generador de calidad lo suficientemente alta, también puede minstd_rand fácilmente. P.ej:

  typedef std::mt19937 G; // Now using mersenne_twister_engine 

Tener un control separado sobre el generador de números aleatorios, y la distribución aleatoria puede ser bastante liberador.

También he calculado (no mostrado) los primeros 4 "momentos" de esta distribución (usando minstd_rand ) y los minstd_rand con los valores teóricos en un bash de cuantificar la calidad de la distribución:

 min = -57 max = 365 mean = 154.131 x_mean = 154 var = 14931.9 x_var = 14910.7 skew = -0.00197375 x_skew = 0 kurtosis = -1.20129 x_kurtosis = -1.20001 

(El prefijo x_ refiere a "esperado")

Vamos a dividir el problema en dos partes:

  • Genere un número aleatorio n en el rango de 0 a (máximo-mínimo).
  • Agrega un mínimo a ese número

La primera parte es obviamente la más difícil. Supongamos que el valor de retorno de rand () es perfectamente uniforme. El uso de módulo agregará sesgo a los primeros números (RAND_MAX + 1) % (max-min+1) . Entonces, si pudiéramos cambiar mágicamente RAND_MAX a RAND_MAX - (RAND_MAX + 1) % (max-min+1) , ya no existiría ningún sesgo.

Resulta que podemos usar esta intuición si estamos dispuestos a permitir el pseudo-no-determinismo en el tiempo de ejecución de nuestro algoritmo. Cuando rand () devuelve un número que es demasiado grande, simplemente solicitamos otro número aleatorio hasta que obtengamos uno que sea lo suficientemente pequeño.

El tiempo de ejecución ahora está distribuido geométricamente , con el valor esperado 1/p donde p es la probabilidad de obtener un número suficientemente pequeño en el primer bash. Como RAND_MAX - (RAND_MAX + 1) % (max-min+1) siempre es menor que (RAND_MAX + 1) / 2 , sabemos que p > 1/2 , por lo que el número esperado de iteraciones siempre será menor que dos para cualquier rango Debería ser posible generar decenas de millones de números aleatorios en menos de un segundo en una CPU estándar con esta técnica.

EDITAR:

Aunque lo anterior es técnicamente correcto, la respuesta de DSimon es probablemente más útil en la práctica. No deberías implementar esto tú mismo. He visto muchas implementaciones de muestreo de rechazo y a menudo es muy difícil ver si es correcto o no.

¿Qué tal el Mersenne Twister ? La implementación de impulso es bastante fácil de usar y está bien probada en muchas aplicaciones del mundo real. Lo he usado yo mismo en varios proyectos académicos, como inteligencia artificial y algoritmos evolutivos.

Aquí está su ejemplo donde hacen una función simple para lanzar un dado de seis caras:

 #include  #include  #include  boost::mt19937 gen; int roll_die() { boost::uniform_int<> dist(1, 6); boost::variate_generator > die(gen, dist); return die(); } 

Ah, y aquí hay un poco de proxenetismo de este generador por si acaso no está convencido de que debería usarlo sobre el rand() inmensamente inferior rand() :

El Mersenne Twister es un generador de “números aleatorios” inventado por Makoto Matsumoto y Takuji Nishimura; su sitio web incluye numerosas implementaciones del algoritmo.

Esencialmente, el Mersenne Twister es un registro de desplazamiento de retroalimentación lineal muy grande. El algoritmo opera en una semilla de 19,937 bits, almacenada en una matriz de 624 elementos de enteros sin signo de 32 bits. El valor 2 ^ 19937-1 es un primo de Mersenne; la técnica para manipular la semilla se basa en un antiguo algoritmo de “torsión”, de ahí el nombre “Mersenne Twister”.

Un aspecto atractivo del Mersenne Twister es su uso de operaciones binarias, en oposición a la multiplicación que consume mucho tiempo, para generar números. El algoritmo también tiene un período muy largo y buena granularidad. Es rápido y efectivo para aplicaciones no criptográficas.

 int RandU(int nMin, int nMax) { return nMin + (int)((double)rand() / (RAND_MAX+1) * (nMax-nMin+1)); } 

Este es un mapeo de enteros 32768 a enteros (nMax-nMin + 1). El mapeo será bastante bueno si (nMax-nMin + 1) es pequeño (como en su requerimiento). Sin embargo, tenga en cuenta que si (nMax-nMin + 1) es grande, la asignación no funcionará (por ejemplo, no puede asignar 32768 valores a 30000 valores con la misma probabilidad). Si se necesitan tales rangos, debe usar una fuente aleatoria de 32 o 64 bits, en lugar de los resultados de rand () de 15 bits o ignorar los resultados de rand () que están fuera del rango.

Aquí hay una versión imparcial que genera números en [low, high] :

 int r; do { r = rand(); } while (r < ((unsigned int)(RAND_MAX) + 1) % (high + 1 - low)); return r % (high + 1 - low) + low; 

Si su rango es razonablemente pequeño, no hay razón para almacenar el lado derecho de la comparación en el bucle do .

Recomiendo la biblioteca Boost.Random , es súper detallada y está bien documentada, le permite especificar de manera explícita qué distribución desea, y en escenarios no criptográficos puede superar la típica implementación de rand de la biblioteca C.

supongamos que min y max son valores int, [y] significa que incluyen este valor, (y) significa que no incluyen este valor, usando arriba para obtener el valor correcto usando c ++ rand ()

referencia: para () [] definir, visitar:

https://en.wikipedia.org/wiki/Interval_(mathematics)

para la función rand y srand o RAND_MAX define, visita:

http://en.cppreference.com/w/cpp/numeric/random/rand

[mínimo máximo]

 int randNum = rand() % (max - min + 1) + min 

(mínimo máximo]

 int randNum = rand() % (max - min) + min + 1 

[mínimo máximo)

 int randNum = rand() % (max - min) + min 

(mínimo máximo)

 int randNum = rand() % (max - min - 1) + min + 1 

En este tema, el muestreo de rechazo ya fue discutido, pero quería sugerir una optimización basada en el hecho de que rand() % 2^something no introduce ningún sesgo como ya se mencionó anteriormente.

El algoritmo es realmente simple:

  • calcule la potencia más pequeña de 2 mayor que la longitud del intervalo
  • aleatorizar un número en ese “nuevo” intervalo
  • devuelve ese número si es menor que la longitud del intervalo original
    • rechazar de otra manera

Aquí está mi código de muestra:

 int randInInterval(int min, int max) { int intervalLen = max - min + 1; //now calculate the smallest power of 2 that is >= than `intervalLen` int ceilingPowerOf2 = pow(2, ceil(log2(intervalLen))); int randomNumber = rand() % ceilingPowerOf2; //this is "as uniform as rand()" if (randomNumber < intervalLen) return min + randomNumber; //ok! return randInInterval(min, max); //reject sample and try again } 

Esto funciona bien especialmente para intervalos pequeños, porque la potencia de 2 estará "más cerca" de la longitud del intervalo real, por lo que el número de errores será menor.

PD
Obviamente, evitar la recursividad sería más eficiente (no es necesario calcular una y otra vez el techo del registro), pero pensé que era más legible para este ejemplo.

La fórmula para esto es muy simple, así que prueba esta expresión,

  int num = (int) rand() % (max - min) + min; //Where rand() returns a random number between 0.0 and 1.0 

La siguiente expresión debería ser imparcial si no me equivoco:

 std::floor( ( max - min + 1.0 ) * rand() ) + min; 

Estoy asumiendo aquí que rand () te da un valor aleatorio en el rango entre 0.0 y 1.0 que NO incluye 1.0 y que max y min son enteros con la condición de que min