c++ - ¿Cómo reduzco los números desde rand()?
random generator (9)
¿Cuánto tiempo le gustaría una respuesta?
lo más simple es convertir utilizando el resto cuando se divide por 101:
int value = rawRand % 101;
Un semipurista reescalaría usando dobles:
double dbl = 100 * ((double)rawRand / RAND_MAX);
int ivalue = (int)(dbl + 0.5); // round up for above 0.5
Y un purista diría que rand no produce números aleatorios.
Para su información, la calidad de los números aleatorios se mide tomando una secuencia de números y luego calculando la probabilidad matemática de que la fuente de esa secuencia sea aleatoria. El truco simple utilizando el resto es una opción muy pobre si buscas la aleatoriedad.
El siguiente código genera un número aleatorio cada segundo:
int main ()
{
srand(time(NULL)); // Seeds number generator with execution time.
while (true)
{
int rawRand = rand();
std::cout << rawRand << std::endl;
sleep(1);
}
}
¿Cómo puedo clasificar estos números para que estén siempre en el rango de 0-100?
Algunas personas han publicado el siguiente código como ejemplo:
int rawRand = (rand() / RAND_MAX) * 100;
Esta es una forma inválida de resolver el problema, ya que tanto rand () como RAND_MAX son enteros. En C ++, esto da como resultado una división integral, que truncará los puntos decimales de los resultados. Como RAND_MAX> = rand (), el resultado de esa operación es 1 o 0, lo que significa que RawRand puede ser solo 0 o 100. Una forma correcta de hacerlo sería la siguiente:
int rawRand = (rand() / static_cast<double>(RAND_MAX)) * 100;
Como uno de los operandos ahora es una división doble, de punto flotante, que devolvería un valor adecuado entre 0 y 1.
Para el rango de mínimo a máximo (inclusive), use: int result = rand() % (max - min + 1) + min;
Si está usando C ++ y le preocupa la buena distribución, puede usar TR1 C ++ 11 <random>
.
#include <random>
std::random_device rseed;
std::mt19937 rgen(rseed()); // mersenne_twister
std::uniform_int_distribution<int> idist(0,100); // [0,100]
std::cout << idist(rgen) << std::endl;
Todos los ejemplos publicados hasta ahora realmente dan resultados mal distribuidos. Ejecute el código con frecuencia y cree una estadística para ver cómo se vuelven sesgados los valores.
Una mejor manera de generar una distribución aleatoria de números real y uniforme en cualquier rango [0, N ] es la siguiente (suponiendo que rand
realidad sigue una distribución uniforme, lo que está lejos de ser obvio):
unsigned result;
do {
result = rand();
} while (result > N);
Por supuesto, ese método es lento pero produce una buena distribución. Una forma un poco más inteligente de hacer esto es encontrar el múltiplo más grande de N que sea más pequeño que RAND_MAX
y usarlo como el límite superior. Después de eso, uno puede tomar con seguridad el result % (N + 1)
.
Para una explicación de por qué el método de módulo ingenuo es malo y por qué lo anterior es mejor, consulte el excelente artículo de Julienne sobre el uso de rand
.
Tu puedes hacer
cout << rawRand % 100 << endl; // Outputs between 0 and 99
cout << rawRand % 101 << endl; // outputs between 0 and 100
Para la gente que vota menos; nota un minuto después de que esto fue publicado originalmente dejé el comentario:
De http://www.cplusplus.com/reference/clibrary/cstdlib/rand "Tenga en cuenta que esta operación de módulo no genera un número aleatorio distribuido de manera uniforme en el tramo (ya que en la mayoría de los casos los números más bajos son ligeramente más probables), pero generalmente es una buena aproximación para tramos cortos ".
Con entradas de 64 bits y usando 100 números como salida, los números 0-16 se representan con 1.00000000000000000455% de los números (una precisión relativa para distribuir de forma idéntica del 1% entre aproximadamente 10 -18 ), mientras que los números 17-99 están representados. con 0.99999999999999999913% de los números. Sí, no perfectamente distribuido, pero una muy buena aproximación para pequeños tramos.
También tenga en cuenta, ¿dónde solicita el OP números idénticamente distribuidos? Por todo lo que sabemos, estos están siendo utilizados para propósitos en los que las pequeñas desviaciones no importan (por ejemplo, cualquier otra cosa que no sea la criptografía, y si usan los números para la criptografía, esta pregunta es demasiado ingenua para que escriban su propia criptografía )
EDITAR - Para las personas que realmente están interesadas en tener una distribución uniforme de números aleatorios, el siguiente código funciona. Tenga en cuenta que esto no es necesariamente óptimo ya que con las entradas aleatorias de 64 bits, requerirá dos llamadas de rand()
una vez cada 10 ^ 18 llamadas.
unsigned N = 100; // want numbers 0-99
unsigned long randTruncation = (RAND_MAX / N) * N;
// include every number the N times by ensuring rawRand is between 0 and randTruncation - 1 or regenerate.
unsigned long rawRand = rand();
while (rawRand >= randTruncation) {
rawRand = rand();
// with 64-bit int and range of 0-99 will need to generate two random numbers
// about 1 in every (2^63)/16 ~ 10^18 times (1 million million times)
// with 32-bit int and range of 0-99 will need to generate two random numbers
// once every 46 million times.
}
cout << rawRand % N << stdl::endl;
Vea man 3 rand
- necesita escalar dividiendo por RAND_MAX
para obtener el rango [0, 1] después del cual puede multiplicar por 100 para su rango objetivo.
rawRand% 101 daría [0-100], inclusive.
int rawRand = rand() % 101;
Ver (para más detalles):
Otros también han señalado que esto no le dará la mejor distribución de números aleatorios posible. Si ese tipo de cosas es importante en tu código, tendrías que hacer:
int rawRand = (rand() * 1.0 / RAND_MAX) * 100;
EDITAR
Tres años después, estoy haciendo una edición. Como otros mencionaron, rand()
tiene una gran cantidad de problemas. Obviamente, no puedo recomendar su uso cuando hay mejores alternativas en el futuro. Puede leer todos los detalles y recomendaciones aquí: