c++ - randomize - Genere números aleatorios uniformemente en un rango completo
random double c++ (15)
Necesito generar números aleatorios dentro de un intervalo específico, [max; min].
Además, los números aleatorios deben distribuirse uniformemente durante el intervalo, no ubicados en un punto particular.
Currenly estoy generando como:
for(int i=0; i<6; i++)
{
DWORD random = rand()%(max-min+1) + min;
}
De mis pruebas, los números aleatorios se generan alrededor de un punto solamente.
Example
min = 3604607;
max = 7654607;
Números aleatorios generados:
3631594
3609293
3630000
3628441
3636376
3621404
De las respuestas a continuación: OK, RAND_MAX es 32767. Estoy en la plataforma C ++ de Windows. ¿Hay algún otro método para generar números aleatorios con una distribución uniforme?
Por qué rand
es una mala idea
La mayoría de las respuestas que obtuvo aquí utilizan la función rand
y el operador de módulo. Ese método puede no generar números de manera uniforme (depende del rango y el valor de RAND_MAX
) y, por lo tanto, se desaconseja.
C ++ 11 y generación en un rango
Con C ++ 11, muchas otras opciones han aumentado. Una de ellas se ajusta a sus requisitos, para generar un número aleatorio en un rango, muy bien: std::uniform_int_distribution
. Aquí hay un ejemplo:
const int range_from = 0;
const int range_to = 10;
std::random_device rand_dev;
std::mt19937 generator(rand_dev());
std::uniform_int_distribution<int> distr(range_from, range_to);
std::cout << distr(generator) << ''/n'';
Y here está el ejemplo corriente.
Otros generadores aleatorios
El encabezado <random>
ofrece innumerables otros generadores de números aleatorios con diferentes tipos de distribuciones, incluyendo Bernoulli, Poisson y normal.
¿Cómo puedo barajar un contenedor?
El estándar proporciona std::random_shuffle
, que se puede usar de la siguiente manera:
std::vector<int> vec = {4, 8, 15, 16, 23, 42};
std::random_device random_dev;
std::mt19937 generator(random_dev());
std::shuffle(vec.begin(), vec.end(), generator);
El algoritmo reordenará los elementos al azar, con una complejidad lineal.
Boost.Random
Otra alternativa, en caso de que no tenga acceso a un compilador C ++ Boost.Random , es usar Boost.Random . Su interfaz es muy similar a la de C ++ 11.
Acabo de encontrar esto en Internet. Esto debería funcionar:
DWORD random = ((min) + rand()/(RAND_MAX + 1.0) * ((max) - (min) + 1));
Comprueba qué RAND_MAX está en tu sistema: supongo que solo son 16 bits y tu alcance es demasiado grande.
Más allá de eso, vea esta discusión sobre: Generar enteros aleatorios dentro de un rango deseado y las notas sobre el uso (o no) de la función C rand () .
Debería mirar RAND_MAX para su compilador / entorno particular. Creo que vería estos resultados si rand () está produciendo un número aleatorio de 16 bits. (Parece que asumes que será un número de 32 bits).
No puedo prometer que esta es la respuesta, pero publique su valor de RAND_MAX y un poco más de detalles sobre su entorno.
Este no es el código, pero esta lógica puede ayudarte.
static double rnd(void)
{
return (1.0/(RAND_MAX+1.0)*((double)(rand())) );
}
static void InitBetterRnd(unsigned int seed)
{
register int i;
srand( seed );
for( i=0; i<POOLSIZE; i++){
pool[i]= rnd();
}
}
static double rnd0_1(void)
{ // This function returns a number between 0 and 1
static int i=POOLSIZE-1;
double r;
i = (int)(POOLSIZE*pool[i]);
r=pool[i];
pool[i]=rnd();
return (r);
}
Esto debería proporcionar una distribución uniforme en el rango [low, high)
sin usar flotadores, siempre que el rango general sea menor que RAND_MAX.
uint32_t rand_range_low(uint32_t low, uint32_t high)
{
uint32_t val;
// only for 0 < range <= RAND_MAX
assert(low < high);
assert(high - low <= RAND_MAX);
uint32_t range = high-low;
uint32_t scale = RAND_MAX/range;
do {
val = rand();
} while (val >= scale * range); // since scale is truncated, pick a new val until it''s lower than scale*range
return val/scale + low;
}
y para valores superiores a RAND_MAX quieres algo como
uint32_t rand_range(uint32_t low, uint32_t high)
{
assert(high>low);
uint32_t val;
uint32_t range = high-low;
if (range < RAND_MAX)
return rand_range_low(low, high);
uint32_t scale = range/RAND_MAX;
do {
val = rand() + rand_range(0, scale) * RAND_MAX; // scale the initial range in RAND_MAX steps, then add an offset to get a uniform interval
} while (val >= range);
return val + low;
}
Esto es más o menos cómo std :: uniform_int_distribution hace las cosas.
La solución dada por man 3 rand para un número entre 1 y 10 inclusive es:
j = 1 + (int) (10.0 * (rand() / (RAND_MAX + 1.0)));
En tu caso, sería:
j = min + (int) ((max-min+1) * (rand() / (RAND_MAX + 1.0)));
Por supuesto, esto no es una aleatoriedad o uniformidad perfecta, como señalan algunos otros mensajes, pero esto es suficiente para la mayoría de los casos.
Me gustaría complementar las excelentes respuestas de Angry Shoe y peterchen con una breve descripción del estado del arte en 2015:
Algunas buenas elecciones
randutils
La biblioteca de randutils
(presentation) es una novedad interesante, que ofrece una interfaz simple y (declarada) capacidades robustas aleatorias. Tiene las desventajas de que agrega una dependencia de su proyecto y, al ser nuevo, no se ha probado exhaustivamente. De todos modos, siendo libre (licencia de MIT) y encabezado solo, creo que vale la pena intentarlo.
Muestra mínima: una tirada de dado
#include <iostream>
#include "randutils.hpp"
int main() {
randutils::mt19937_rng rng;
std::cout << rng.uniform(1,6) << "/n";
}
Incluso si uno no está interesado en la biblioteca, el sitio web ( http://www.pcg-random.org/ ) ofrece muchos artículos interesantes sobre el tema de la generación de números aleatorios en general y la biblioteca C ++ en particular.
Boost.Random
Boost.Random (documentation) es la biblioteca que inspiró <random>
C ++ 11, con quien comparte gran parte de la interfaz. Aunque teóricamente también es una dependencia externa, Boost tiene ahora un estado de biblioteca "cuasi-estándar", y su módulo Random podría considerarse como la opción clásica para la generación de números aleatorios de buena calidad. Presenta dos ventajas con respecto a la solución C ++ 11:
- es más portátil, solo necesita compatibilidad de compilador para C ++ 03
- su
random_device
usa métodos específicos del sistema para ofrecer siembra de buena calidad
El único pequeño inconveniente es que el módulo que ofrece el random_device
no es solo de encabezado, uno tiene que compilar y vincular boost_random
.
Muestra mínima: una tirada de dado
#include <iostream>
#include <boost/random.hpp>
#include <boost/nondet_random.hpp>
int main() {
boost::random::random_device rand_dev;
boost::random::mt19937 generator(rand_dev());
boost::random::uniform_int_distribution<> distr(1, 6);
std::cout << distr(generator) << ''/n'';
}
Si bien la muestra mínima funciona bien, los programas reales deberían usar un par de mejoras:
- make
mt19937
athread_local
: el generador es bastante regordete (> 2 KB) y es mejor no asignarlo en la pila - seed
mt19937
con más de un entero: Mersenne Twister tiene un gran estado y puede beneficiarse de más entropía durante la inicialización
Algunas elecciones no tan buenas
La biblioteca C ++ 11
Si bien es la solución más idiomática, la biblioteca <random>
no ofrece mucho a cambio de la complejidad de su interfaz, incluso para las necesidades básicas. El error está en std::random_device
: el estándar no std::random_device
ninguna calidad mínima para su salida (siempre que entropy()
devuelva 0
) y, a partir de 2015, MinGW (no el compilador más utilizado, pero difícilmente una opción esotérica) siempre imprimirá 4
en la muestra mínima.
Muestra mínima: una tirada de dado
#include <iostream>
#include <random>
int main() {
std::random_device rand_dev;
std::mt19937 generator(rand_dev());
std::uniform_int_distribution<int> distr(1, 6);
std::cout << distr(generator) << ''/n'';
}
Si la implementación no está podrida, esta solución debería ser equivalente a la de Boost y se aplican las mismas sugerencias.
La solución de Godot
Muestra mínima: una tirada de dado
#include <iostream>
#include <random>
int main() {
std::cout << std::randint(1,6);
}
Esta es una solución simple, efectiva y ordenada. Solo defecto, tardará un tiempo en compilarse: unos dos años, siempre que C ++ 17 se libere a tiempo y la función experimental randint
esté aprobada en el nuevo estándar. Quizás en ese momento también mejorarán las garantías sobre la calidad de la siembra.
La worse-is-better solución worse-is-better
Muestra mínima: una tirada de dado
#include <cstdlib>
#include <ctime>
#include <iostream>
int main() {
std::srand(std::time(nullptr));
std::cout << (std::rand() % 6 + 1);
}
La antigua solución C se considera dañina, y por buenas razones (consulte las otras respuestas aquí o este análisis detallado ). Aún así, tiene sus ventajas: es simple, portátil, rápido y honesto, en el sentido en que se sabe que los números aleatorios que uno obtiene no son dignos, y por lo tanto uno no está tentado de usarlos para propósitos serios.
La solución de troll contable
Muestra mínima: una tirada de dado
#include <iostream>
int main() {
std::cout << 9; // http://dilbert.com/strip/2001-10-25
}
Mientras que 9 es un resultado algo inusual para una tirada de dado regular, uno tiene que admirar la excelente combinación de buenas cualidades en esta solución, que se las arregla para ser la más rápida, más simple, más amigable con la caché y más portátil. Sustituyendo 9 por 4 se obtiene un generador perfecto para cualquier tipo de mazmorras y dragones mueren, mientras se evitan los valores cargados de símbolos 1, 2 y 3. El único pequeño error es que, debido al mal genio de los trols de contabilidad de Dilbert, este programa en realidad engendra un comportamiento indefinido.
Por su naturaleza, una pequeña muestra de números aleatorios no tiene que estar uniformemente distribuida. Son al azar, después de todo. Estoy de acuerdo en que si un generador de números aleatorios está generando números que aparecen consistentemente agrupados, entonces probablemente haya algo erróneo en él.
Pero tenga en cuenta que la aleatoriedad no es necesariamente uniforme.
Editar: agregué "pequeña muestra" para aclarar.
Si RAND_MAX es 32767, puede duplicar fácilmente el número de bits.
int BigRand()
{
assert(INT_MAX/(RAND_MAX+1) > RAND_MAX);
return rand() * (RAND_MAX+1) + rand();
}
Si desea que los números se distribuyan uniformemente en el rango, debe dividir su rango en varias secciones iguales que representen la cantidad de puntos que necesita. Luego obtenga un número aleatorio con un min / max para cada sección.
Como otra nota, probablemente no deberías usar rand () ya que no es muy bueno para generar números aleatorios. No sé en qué plataforma se está ejecutando, pero probablemente haya una función mejor a la que pueda llamar como random ().
Si le preocupa la aleatoriedad y no la velocidad, debe usar un método seguro de generación de números aleatorios. Hay varias maneras de hacer esto ... La más fácil es usar OpenSSL''s generador de números aleatorios OpenSSL''s .
También puede escribir su propio uso de un algoritmo de encriptación (como AES ). Al elegir una semilla y una IV y luego volver a cifrar continuamente la salida de la función de cifrado. Usar OpenSSL es más fácil, pero menos varonil.
Si puede, use Boost . He tenido buena suerte con su biblioteca al azar .
uniform_int
debería hacer lo que quieras.
@Solution ((double) rand() / (RAND_MAX+1)) * (max-min+1) + min
Advertencia : no olvide que debido a los estiramientos y posibles errores de precisión (incluso si RAND_MAX fuera lo suficientemente grande), solo podrá generar "contenedores" distribuidos uniformemente y no todos los números en [min, max].
@Solución: Bigrand
Advertencia : Tenga en cuenta que esto duplica los bits, pero aún así no podrá generar todos los números en su rango en general, es decir, no es necesariamente cierto que BigRand () generará todos los números en su rango.
Información : Su enfoque (módulo) está "bien" siempre que el rango de rand () exceda su rango de intervalo y rand () sea "uniforme". El error para, como máximo, los primeros números máximos - min es 1 / (RAND_MAX +1).
Además, sugiero cambiar al nuevo paquete aleatorio e en C ++ 11 también, que ofrece mejores y más variedades de implementaciones que rand ().
[edit] Advertencia: No use rand()
para estadísticas, simulación, criptografía o cualquier cosa seria.
Es lo suficientemente bueno para hacer que los números parezcan aleatorios para un humano típico a toda prisa, nada más.
Consulte la respuesta de @ Jefffrey para obtener mejores opciones, o esta respuesta para números aleatorios criptográficos.
En general, los bits altos muestran una mejor distribución que los bits bajos, por lo que la forma recomendada de generar números aleatorios de un rango para fines simples es:
((double) rand() / (RAND_MAX+1)) * (max-min+1) + min
Nota : asegúrese de que RAND_MAX + 1 no se desborde (¡gracias Demi)!
La división genera un número aleatorio en el intervalo [0, 1); "estirar" esto al rango requerido. Solo cuando max-min + 1 se acerca a RAND_MAX necesita una función "BigRand ()" como la publicada por Mark Ransom.
Esto también evita algunos problemas de corte debido al módulo, lo que puede empeorar aún más tus números.
No se garantiza que el generador de números aleatorios incorporado tenga la calidad requerida para las simulaciones estadísticas. Está bien que los números "parezcan aleatorios" a un ser humano, pero para una aplicación seria, debes tomar algo mejor, o al menos verificar sus propiedades (la distribución uniforme suele ser buena, pero los valores tienden a correlacionarse, y la secuencia es determinista ) Knuth tiene un tratado excelente (aunque difícil de leer) sobre generadores de números aleatorios, y recientemente encontré que LFSR es excelente y muy fácil de implementar, dado que sus propiedades son buenas para usted.