2010-06-08 24 views
13

Duplicar posible:
Generating Random Numbers in Objective-C¿Generar un número aleatorio dentro del rango?

¿Cómo se genera un número aleatorio que está dentro de un rango?

+4

Duplicado - f.e. http://stackoverflow.com/questions/160890/generating-random-numbers-in-objective-c – schnaader

+0

¿Intentó buscar? Esto se ha pedido una buena media docena de veces antes. Además, todas las respuestas a continuación son incorrectas en el sentido de que 'rand' no es un buen RNG y requiere siembra. Deberías usar 'arc4random' en su lugar. –

+0

@Dave: no hay garantía de que 'rand()' sea necesariamente malo, simplemente no hay garantía de que sea bueno. Recomendar arc4random sin conocer el propósito previsto tiene poco sentido (si es que tiene alguno). La mayoría de los principiantes no están haciendo nada donde la calidad del generador realmente importa de todos modos. –

Respuesta

52

Esto es en realidad un poco más difícil de conseguir realmente correcto que la mayoría de las personas se dan cuenta de:

int rand_lim(int limit) { 
/* return a random number between 0 and limit inclusive. 
*/ 

    int divisor = RAND_MAX/(limit+1); 
    int retval; 

    do { 
     retval = rand()/divisor; 
    } while (retval > limit); 

    return retval; 
} 

Los intentos que simplemente usan % (o, equivalentemente, /) para obtener los números en un rango casi inevitablemente introducen sesgos (es decir, algunos números se generarán con más frecuencia que otros).

En cuanto a por qué usar % produce resultados asimétricos: a menos que el rango que desea sea un divisor de RAND_MAX, el sesgo es inevitable. Si comienzas con números pequeños, es bastante fácil ver por qué. Considere tomar 10 dulces e intente dividirlos en partes iguales entre tres niños. Claramente no se puede hacer, si repartes todos los dulces, lo más cerca que puedes conseguir es que dos niños reciban tres dulces y uno de ellos cuatro.

Solo hay una manera para que todos los niños reciban la misma cantidad de caramelos: asegúrese de no entregar la última pieza de caramelo.

Para relacionar esto con el código anterior, comencemos enumerando los dulces del 1 al 10 y los niños del 1 al 3. La división inicial dice que hay tres niños, nuestro divisor es tres. Luego sacamos un caramelo al azar de la cubeta, miramos su número y lo dividimos entre tres y se lo damos a ese niño, pero si el resultado es mayor que 3 (es decir, hemos elegido el caramelo número 10) simplemente no lo hacemos. repartirlo en absoluto: lo descartamos y elegimos otro caramelo.

Por supuesto, si está utilizando una implementación moderna de C++ (es decir, una que admite C++ 11 o posterior), normalmente debería usar una de las clases distribution de la biblioteca estándar. El código anterior corresponde más estrechamente con std::uniform_int_distribution, pero la biblioteca estándar también incluye uniform_real_distribution, así como las clases para una serie de distribuciones no uniformes (Bernoulli, Poisson, normal, tal vez un par de otros que no recuerdo en este momento).

+5

Lo siento, pero ewww! Lo último que quiero hacer es dividir un número indeterminado (quién sabe, quizás para siempre) cuando intenta obtener un número aleatorio. Sesgo o no, tomaré una solución más simple o tal vez una basada en tablas sobre esto. –

+3

@Michael: pruébalo antes de saltar a cualquier conclusión. De hecho, el "número indeterminado" es * generalmente * 1. A menos que haga esto en un bucle * realmente * apretado (por ejemplo, llenando un vector con números aleatorios) es virtualmente imposible siquiera medir esto siendo más lento que las versiones que son claramente incorrecto. –

+4

Esto es técnicamente correcto pero tiene un problema GIGO comenzando con rand(). Parece que estás tratando de corregir el sesgo cuando el límite no es un factor de RAND_MAX, usando el enfoque de división modular tu error está en el orden de 'limit/RAND_MAX' hasta que' limit' se acerca a 'RAND_MAX'. Los problemas comienzan con un PRNG como Rand() efectos enanos como ese. El precio que paga es introducir un ciclo no derterminístico con tiempo de ejecución ilimitado [IRL se ejecuta una vez pero estamos dividiendo pelos aquí ;-)] y no gana nada porque Rand es una mierda para empezar. – Ukko

7
int rand_range(int min_n, int max_n) 
{ 
    return rand() % (max_n - min_n + 1) + min_n; 
} 

Para fracciones:

double rand_range(double min_n, double max_n) 
{ 
    return (double)rand()/RAND_MAX * (max_n - min_n) + min_n; 
} 
+4

Esto no dará una distribución uniforme (a excepción de las potencias-de-dos). –

+2

Lo sé, pero funciona rápido, y la distribución está bien para rangos pequeños. Es bueno para la codificación de juegos. De todos modos, debería haber escrito esto en alguna parte de la respuesta. –

+1

@Oli ¿a quién le importa? Ver el comentario de Ukko en la respuesta anterior. Si está usando rand() para empezar, la pequeña cantidad de sesgo (para max_n-min_n relativamente pequeño) es la menor de sus preocupaciones. – user168715

-1

escribí esto específicamente en Obj-C para un proyecto de iPhone:

- (int) intInRangeMinimum:(int)min andMaximum:(int)max { 
    if (min > max) { return -1; } 
    int adjustedMax = (max + 1) - min; // arc4random returns within the set {min, (max - 1)} 
    int random = arc4random() % adjustedMax; 
    int result = random + min; 
    return result; 
} 

de usar:

int newNumber = [aClass intInRangeMinimum:1 andMaximum:100]; 

Añadir sal al gusto

+0

Esa solución todavía tiene el sesgo introducido por la aritmética modular. Dado que está utilizando la biblioteca arc4, le proporciona exactamente la rutina que desea llamar 'arc4random_uniform' que da resultados adecuadamente uniformes. simplemente cambie la línea a int random = arc4random_uniform (adjustedMax); –

2

Para un valor entero en el rango [ min, max):

double scale = (double) (max - min)/RAND_MAX; 
int val = min + floor(rand() * scale) 
+2

para los newbs, tendrá que incluir '#include ' –

-2
+(NSInteger)randomNumberWithMin:(NSInteger)min WithMax:(NSInteger)max { 
    if (min>max) { 
     int tempMax=max; 
     max=min; 
     min=tempMax; 
    } 
    int randomy=arc4random() % (max-min+1); 
    randomy=randomy+min; 
    return randomy; 
} 

Utilizo este método en un número aleatorio relacionado con la clase que hice. Funciona bien para mis necesidades no exigentes, pero bien puede ser parcial de alguna manera.

+0

Esto se parece más a Objective-C que a pure-C. –

Cuestiones relacionadas