2010-07-23 14 views
80

Digamos que tengo una matriz con objetos, 1, 2, 3 y 4. ¿Cómo elegiría un objeto aleatorio de esta matriz?Escogiendo un objeto aleatorio en un NSArray

+0

Todas las respuestas aquí son correctas, pero para obtener una solución más actualizada, consulte mi respuesta [aquí] (http://stackoverflow.com/a/10837462/730701). Utiliza el método 'arc4random_uniform' para evitar el sesgo de módulo. – Adam

Respuesta

184

@ respuesta de Darryl es correcta, pero podría utilizar algunos ajustes menores:

NSUInteger randomIndex = arc4random() % [theArray count]; 

Modificaciones:

  • Uso arc4random() sobre rand() y random() es más sencillo, ya que no requiere de la siembra (llamando srand() o srandom()).
  • modulo operator (%) hace que la declaración general sea más corta, a la vez que la hace semánticamente más clara.
  • theArray.count es incorrecto. Funcionará, pero count no está declarado como @property en NSArray, y por lo tanto no se invocará a través de la sintaxis de puntos. Que funcione es simplemente un efecto secundario de cómo el compilador interpreta la sintaxis de punto.
+2

Tenga en cuenta que RC4/ARC4 no da salida uniforme. –

+43

"theArray.count está equivocado. Funcionará, pero el recuento no se declara como @property en NSArray y, por lo tanto, no se debe invocar mediante la sintaxis de punto". --- Esto no es correcto La sintaxis de puntos y las propiedades declaradas no están realmente relacionadas: puede usar la sintaxis de puntos en métodos sin argumentos sin ningún problema. –

+27

Desde la página de manual de arc4random: se recomienda arc4random_uniform() sobre construcciones como '' arc4random()% upper_bound '' ya que evita el "sesgo de módulo" cuando el límite superior no es una potencia de dos. –

1

Genere un número aleatorio y úselo como índice. Ejemplo:

#import <Foundation/Foundation.h> 

int main(int argc, const char * argv[]) 
{ 
    @autoreleasepool { 
     NSArray *array = [NSArray arrayWithObjects: @"one", @"two", @"three", @"four", nil]; 
     NSUInteger randomNumber; 
     int fd = open("/dev/random", O_RDONLY); 
     if (fd != -1) { 
      read(fd, &randomNumber, sizeof(randomNumber)); 
      close(fd); 
     } else { 
      fprintf(stderr, "Unable to open /dev/random: %s\n", strerror(errno)); 
      return -1; 
     } 
     double scaledRandomNumber = ((double)randomNumber)/NSUIntegerMax * [array count]; 
     NSUInteger randomIndex = (NSUInteger)floor(scaledRandomNumber); 
     NSLog(@"random element: %@", [array objectAtIndex: randomIndex]); 
    } 
    return 0; 
} 
+0

¡Tan simple y sin embargo funciona tan bien! – Joshua

+0

@Joshua si quiere un poco más de detalle, puede usar 'SecRandomCopyBytes()' para obtener números aleatorios criptográficamente útiles, de todos modos en el iPhone. En Mac, tienes acceso directo a/dev/random. –

+0

Un número aleatorio debería ser suficiente, pero gracias de todos modos por la información adicional. – Joshua

10

Tal vez algo en la línea de:

NSUInteger randomIndex = (NSUInteger)floor(random()/RAND_MAX * [theArray count]); 

No se olvide de inicializar el generador de números aleatorios (srandomdev(), por ejemplo).

NOTA: He actualizado para usar -count en lugar de sintaxis punto, por la respuesta a continuación.

0
srand([[NSDate date] timeIntervalSince1970]); 

int inx =rand()%[array count]; 

inx es el número aleatorio.

donde srand() puede estar en cualquier lugar del programa antes de la función de selección aleatoria.

16

Esta es la solución más simple que podía llegar a:

id object = array.count == 0 ? nil : array[arc4random_uniform(array.count)]; 

Es necesario comprobar count debido a un no nil pero vacío NSArray regresará 0 para count y arc4random_uniform(0) vuelve 0. Entonces sin el cheque, saldrás de límites en la matriz.

Esta solución es tentador, pero es mal porque va a causar un accidente con una matriz vacía:

id object = array[arc4random_uniform(array.count)]; 

Como referencia, aquí está la documentation:

página
u_int32_t 
arc4random_uniform(u_int32_t upper_bound); 

arc4random_uniform() will return a uniformly distributed random number less than upper_bound. 

El hombre doesn' Mencione que arc4random_uniform devuelve 0 cuando 0 se pasa como upper_bound.

También, arc4random_uniform se define en <stdlib.h>, pero no fue necesario agregar el #import en mi programa de prueba de iOS.

9
@interface NSArray<ObjectType> (Random) 
- (nullable ObjectType)randomObject; 
@end 

@implementation NSArray (Random) 

- (nullable id)randomObject 
{ 
    id randomObject = [self count] ? self[arc4random_uniform((u_int32_t)[self count])] : nil; 
    return randomObject; 
} 

@end 

Editar: Actualización de Xcode 7. Los medicamentos genéricos, nulabilidad

0
ObjectType *objectVarName = [array objectAtIndex:arc4random_uniform((int)(array.count - 1))]; 

si quieres lanzar que a un int, aquí está la solución para eso (útil para cuando se necesita un int azar de una matriz de números no secuenciales, en el caso de la aleatorización de una llamada enum, etc)

int intVarName = (int)[(NSNumber *)[array objectAtIndex:arc4random_uniform((int)(array.count - 1))] integerValue]; 
0

en Swift 4:

let array = ["one","two","three","four"] 
let randomNumber = arc4random_uniform(UInt32(array.count)) 

array[Int(randomNumber)] 
+0

Revise [Cómo escribo una buena respuesta] (https://stackoverflow.com/help/how-to-answer). Las respuestas de solo código se desalientan porque no explican cómo resuelven el problema en la pregunta. Debe actualizar su respuesta para explicar lo que hace y cómo mejora en las muchas respuestas que esta pregunta de 7 años ya tiene – FluffyKitten