Elegir un objeto aleatorio en un NSArray


83

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


Todas las respuestas aquí son correctas, pero para obtener una solución más actualizada, consulte mi respuesta aquí . Utiliza el arc4random_uniformmétodo para evitar el sesgo de módulo.
Adam

no es una respuesta para esta pregunta, pero sí un punto interesante: otras colecciones de Foundation (NSSet NSHashTable) tienen métodos "anyObject" que leen un objeto arbitrario (aleatorio) del Set / HashTable. Se podría implementar este método en una extensión de NSArray, siguiendo las sugerencias a continuación.
Motti Shneor

Respuestas:


192

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

NSUInteger randomIndex = arc4random() % theArray.count;

Modificaciones:

  • Usar arc4random()over rand()y random()es más simple porque no requiere sembrar (llamar srand()o srandom()).
  • El operador de módulo ( %) hace que la declaración general sea más corta, al mismo tiempo que la hace más clara semánticamente.

27
De la página de manual de arc4random: arc4random_uniform () se recomienda 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.
Max Yankov

@collibhoy no, porque 0 % 4 = 0, 1 % 4 = 1, 2 % 4 = 2, 3 % 4 = 3, 4 % 4 = 0, 5 % 4 = 1... Si Modulo por n , el resultado más grande nunca será mayor que n-1 .
Dave DeLong

1
@DaveDeLong Según el código fuente, countes una propiedad:@interface NSArray<__covariant ObjectType> : NSObject <NSCopying, NSMutableCopying, NSSecureCoding, NSFastEnumeration> @property (readonly) NSUInteger count;
mikeho

17

Esta es la solución más simple que pude encontrar:

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

Es necesario comprobar countdebido a un no nilpero vacío NSArrayvolverá 0para count, y arc4random_uniform(0)vuelve 0. Entonces, sin el cheque, saldrá de los límites de la matriz.

Esta solución es tentadora pero es incorrecta porque provocará un bloqueo con una matriz vacía:

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

Como referencia, aquí está la documentación :

u_int32_t
arc4random_uniform(u_int32_t upper_bound);

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

La página de manual no menciona que arc4random_uniformdevuelve 0cuando 0se pasa como upper_bound.

Además, arc4random_uniformestá definido en <stdlib.h>, pero #importno era necesario agregarlo en mi programa de prueba de iOS.


11

Quizás algo parecido a:

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

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

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


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: actualizado para Xcode 7. Genéricos, nulabilidad


1

Genere un número aleatorio y utilícelo 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;
}

@Joshua, si desea un poco más de detalles, puede usarlo SecRandomCopyBytes()para obtener números aleatorios criptográficamente útiles, en el iPhone de todos modos. En Mac, tiene acceso directo a / dev / random.

Creo que el punto principal de la pregunta es mostrar cómo elegir un elemento aleatorio de la matriz, y esta respuesta realmente no brinda la mejor información.
pico

Me gusta bastante esta broma, pero la voté en contra para ayudar a quienes no la entienden.
Stig Brautaset

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.


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

si desea convertir eso en un int, aquí está la solución para eso (útil para cuando necesita un int aleatorio de una matriz de números no secuenciales, en el caso de aleatorizar 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)]

1
Por favor revise ¿Cómo escribo una buena respuesta ? Las respuestas de solo código no se recomiendan porque no explican cómo resuelven el problema en la pregunta. Debe actualizar su respuesta para explicar qué hace esto y cómo mejora las muchas respuestas que ya tiene esta pregunta de 7 años
FluffyKitten
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.