Necesito una función que genere un número entero aleatorio en un rango determinado (incluidos los valores de borde). No tengo requisitos de calidad / aleatoriedad irracionales, 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 actual de generador 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 desde <0, 1> hasta <-32727, 32727>.
- Tiene que ser visible.
Actualmente tengo el siguiente código C ++:
output = min + (rand() * (int)(max - min) / RAND_MAX)
El problema es que no es realmente uniforme: max solo se devuelve cuando rand () = RAND_MAX (para Visual C ++ es 1/32727). Este es un problema importante para rangos pequeños como <-1, 1>, donde el último valor casi nunca se devuelve.
Así que tomé lápiz y papel y se me ocurrió la siguiente fórmula (que se basa en el truco de redondeo de enteros (int) (n + 0.5)):
Pero todavía no me da una distribución uniforme. Las ejecuciones repetidas con 10000 muestras me dan una relación de 37:50:13 para valores valores -1, 0. 1.
¿Podría por favor sugerir una mejor fórmula? (o incluso la función de generador de números pseudoaleatorios completos)
Respuestas:
Una solución distribuida rápida, algo mejor que la suya, pero aún no uniformemente distribuida es
Excepto cuando el tamaño del rango es una potencia de 2, este método produce números distribuidos no uniformes sesgados independientemente de la calidad de
rand()
. Para una prueba exhaustiva de la calidad de este método, lea esto .fuente
rand()
debería considerarse dañino en C ++, hay formas mucho mejores de obtener algo que esté distribuido de manera uniforme y realmente aleatorio.La respuesta más simple (y por lo tanto mejor) de C ++ (usando el estándar 2011) es
No hay necesidad de reinventar la rueda. No hay que preocuparse por el sesgo. No hay que preocuparse por usar el tiempo como semilla aleatoria.
fuente
random_device
, lo que podría romperse por completo en algunos casos . Además,mt19937
aunque es una muy buena opción de uso general, no es el más rápido de los generadores de buena calidad (vea esta comparación ) y, por lo tanto, podría no ser el candidato ideal para el OP.minstd
será un método así), pero eso es un progreso. En cuanto a la implementación deficienterandom_device
, eso es horrible y debería considerarse un error (posiblemente también del estándar C ++, si lo permite).rand()
no es una opción, y es importante para un uso no crítico, como generar un índice pivote aleatorio? Además, ¿tengo que preocuparme por construirrandom_device
/mt19937
/uniform_int_distribution
en un bucle cerrado / función en línea? ¿Debería preferir pasarlos?Si su compilador admite C ++ 0x y usarlo es una opción para usted,
<random>
es probable que el nuevo encabezado estándar satisfaga sus necesidades. Tiene una alta calidaduniform_int_distribution
que aceptará límites mínimos y máximos (inclusive según lo 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
s aleatorios distribuidos uniformemente en [-57, 365]. He utilizado las nuevas<chrono>
instalaciones estándar para cronometrarlo , ya que mencionó que el rendimiento es una preocupación importante para usted.Para mí (2.8 GHz Intel Core i5) esto imprime:
2.10268e + 07 números aleatorios por segundo.
Puede sembrar el generador pasando un int a su constructor:
Si más tarde descubre que
int
no cubre el rango que necesita para su distribución, esto puede remediarse cambiandouniform_int_distribution
lo siguiente (por ejemplo, along long
):Si más tarde descubre que
minstd_rand
no es un generador de calidad suficientemente alta, eso también puede cambiarse fácilmente. P.ej:Tener un control separado sobre el generador de números aleatorios y la distribución aleatoria puede ser bastante liberador.
También calculé (no se muestran) los primeros 4 "momentos" de esta distribución (usando
minstd_rand
) y los comparé con los valores teóricos en un intento de cuantificar la calidad de la distribución:(El
x_
prefijo se refiere a "esperado")fuente
d
en cada iteración con límites diferentes? ¿Cuánto ralentizaría el ciclo?Dividamos el problema en dos partes:
n
en el rango de 0 a (max-min).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
(RAND_MAX + 1) % (max-min+1)
números. Entonces, si pudiéramos cambiar mágicamenteRAND_MAX
aRAND_MAX - (RAND_MAX + 1) % (max-min+1)
, ya no habrí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. Cada vez que rand () devuelve un número que es demasiado grande, simplemente pedimos otro número aleatorio hasta obtener uno que sea lo suficientemente pequeño.
El tiempo de ejecución ahora se distribuye geométricamente , con el valor esperado
1/p
dondep
está la probabilidad de obtener un número lo suficientemente pequeño en el primer intento. ComoRAND_MAX - (RAND_MAX + 1) % (max-min+1)
siempre es menor que(RAND_MAX + 1) / 2
, lo sabemosp > 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 estas cosas tú mismo. He visto muchas implementaciones de muestreo de rechazo y, a menudo, es muy difícil ver si es correcto o no.
fuente
¿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 lados:
Ah, y aquí hay un poco más de proxenetismo de este generador en caso de que no esté convencido de que debería usarlo sobre el muy inferior
rand()
:fuente
boost::uniform_int
distribución), puedes transformar los rangos min max en lo que quieras, y es visible.Esta es una asignación de 32768 enteros a enteros (nMax-nMin + 1). La asignación será bastante buena si (nMax-nMin + 1) es pequeño (como en su requisito). Sin embargo, tenga en cuenta que si (nMax-nMin + 1) es grande, la asignación no funcionará (por ejemplo, no puede asignar valores de 32768 a valores de 30000 con la misma probabilidad). Si se necesitan tales rangos, debe usar una fuente aleatoria de 32 o 64 bits, en lugar de los 15 bits rand (), o ignorar los resultados de rand () que están fuera de rango.
fuente
RAND_MAX
a(double) RAND_MAX
para evitar la advertencia de desbordamiento de enteros.Aquí hay una versión imparcial que genera números en
[low, high]
:Si su rango es razonablemente pequeño, no hay razón para almacenar en caché el lado derecho de la comparación en el
do
bucle.fuente
[0, h)
por simplicidad. Llamarrand()
tieneRAND_MAX + 1
posibles valores de retorno; tomandorand() % h
colapsos(RAND_MAX + 1) / h
de ellos a cada uno de losh
valores de salida, excepto que(RAND_MAX + 1) / h + 1
estos se asignan a los valores que son menores que(RAND_MAX + 1) % h
(debido al último ciclo parcial a través de lash
salidas). Por lo tanto, eliminamos(RAND_MAX + 1) % h
posibles salidas para obtener una distribución imparcial.Recomiendo la biblioteca Boost.Random , es súper detallada y bien documentada, le permite especificar explícitamente qué distribución desea, y en escenarios no criptográficos puede realmente superar la implementación típica de una biblioteca C rand.
fuente
suponga que min y max son valores int, [y] significa incluir este valor, (y) significa no incluir este valor, utilizando lo anterior para obtener el valor correcto utilizando c ++ rand ()
referencia: para () [] definir, visite:
https://en.wikipedia.org/wiki/Interval_(mathematics)
para la función rand y srand o RAND_MAX define, visite:
http://en.cppreference.com/w/cpp/numeric/random/rand
[mínimo máximo]
(mínimo máximo]
[mínimo máximo)
(mínimo máximo)
fuente
En este hilo, el muestreo de rechazo ya se discutió, 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:
Aquí está mi código de muestra:
Esto funciona bien especialmente para intervalos pequeños, porque la potencia de 2 estará "más cerca" de la longitud real del intervalo, por lo que el número de fallos será menor.
PD:
Obviamente, evitar la recursión sería más eficiente (no es necesario calcular una y otra vez el límite máximo de registro ...) pero pensé que era más legible para este ejemplo.
fuente
Tenga en cuenta que, en la mayoría de las sugerencias, el valor aleatorio inicial que obtiene de la función rand (), que normalmente es de 0 a RAND_MAX, simplemente se desperdicia. Está creando solo un número aleatorio, mientras que hay un procedimiento de sonido que puede brindarle más.
Suponga que desea la región [min, max] de números aleatorios enteros. Comenzamos desde [0, max-min]
Tome la base b = max-min + 1
Comience por representar un número que obtuvo de rand () en la base b.
De esa manera, tiene piso (log (b, RAND_MAX)) porque cada dígito en la base b, excepto posiblemente el último, representa un número aleatorio en el rango [0, max-min].
Por supuesto, el cambio final a [min, max] es simple para cada número aleatorio r + min.
Si NUM_DIGIT es el número de dígitos en la base b que puede extraer y eso es
entonces lo anterior es como una implementación simple de extraer NUM_DIGIT números aleatorios de 0 a b-1 de un número aleatorio RAND_MAX que proporciona b <RAND_MAX.
fuente
La fórmula para esto es muy simple, así que prueba esta expresión,
fuente
int num = (int) rand() % (max - min) + min;
La siguiente expresión debe ser imparcial si no me equivoco:
Asumo aquí que rand () le da un valor aleatorio en el rango entre 0.0 y 1.0 SIN incluir 1.0 y que max y min son enteros con la condición de que min <max.
fuente
std::floor
devuelvedouble
, y necesitamos un valor entero aquí. Me gustaría echar enint
lugar de usarstd::floor
.