2012-06-27 18 views
8

No estoy seguro de cómo funciona el anyObject de NSSet. ¿Qué significa que "El objeto devuelto se elige según la conveniencia del conjunto" (del NSSet class reference)?NSSet cómo extraer objetos aleatoriamente?

Además, ¿cómo puedo extraer los objetos al azar de un NSSet? Estaba pensando en obtener allObjects en una matriz y luego myArray[arc4random_uniform(x)] donde x es la cantidad de objetos en la matriz.

+0

¿Estás de acuerdo con las repeticiones? – Richard

+0

Tengo curiosidad por saberlo yo mismo, supongo que puede llamar a anyObject un número aleatorio de veces, pero su solución de matriz suena mejor. – Patrick

+0

Supongo que convertir NSArray a NSSet de ida y vuelta no será una buena forma de mezclar. –

Respuesta

13

Por lo general, las instancias NSSet se crean con un respaldo CFHash, por lo que casi siempre devuelven el primer objeto en ese hash, ya que es el más rápido para buscar. El motivo dice

El objeto devuelto se elige según la conveniencia del conjunto; no se garantiza que la selección sea aleatoria.

Es porque no siempre se sabe que tendrá una matriz de respaldo. Por lo que usted sabe, la instancia NSSet que tiene tiene un respaldo NSDictionary, o alguna otra estructura de datos similar.

Por lo tanto, en conclusión, si necesita un objeto aleatorio de NSSet, no use -anyObject, en su lugar use allObjects: y luego mezcle esa matriz.

+0

¡Sí! ¡Creo que esto me pone en la tapa de rep para hoy! –

+0

Ahora puede irse a dormir :) – Anne

+0

@Anne no, ¡ahora es el momento de hacerlo en meta! –

4

La documentación dice que anyObject vuelve

Uno de los objetos del conjunto, o nil si el conjunto no contiene objetos. El objeto devuelto se elige según la conveniencia del conjunto; no se garantiza que la selección sea aleatoria.

Lo más probable es que haya algún algoritmo determinista en funcionamiento.

Lo más fiable que hacer sería, como usted sugiere, para crear un NSArray utilizando el método NSSetallObjects, y luego elegir un elemento de azar de que con arc4random() % N donde N es el count del NSArray.

+4

Es mejor usar arc4random_uniform en lugar de usar simplemente el operador de módulo, como fabio sugirió en su pregunta, para evitar el sesgo de módulo. – Sven

14

Presupuesto de NSSet Class Reference:

El objeto devuelto se elige según la conveniencia, la selección del conjunto no se garantiza que sea aleatorio.

Por "aleatoriedad", convierta el NSSet a un NSArray usando [theSet allObjects].
A continuación, elija cualquier objeto aleatoriamente usando arc4random_uniform().

+2

Seamos justos aquí, también tendrá que inicializar el generador de forma aleatoria de forma diferente cada inicio para que sea realmente aleatorio, e incluso entonces, es pseudoaleatorio :-) +1 – trumpetlicks

1

utilizo arc4random(), y dos conjuntos mutables para obtener un conjunto aleatorio y único de objetos:

NSMutableArray *selectionPool = ...; 

int numberOfObjectsToSelect = x; 

NSMutableArray *selectedObjects = [[NSMutableArray alloc] initWithCapacity:numberOfObjectsToSelect]; 

int modulus = selectionPool.count - 1; 

for (int i = 0; i < numberOfObjectsToSelect; i++) { 

    int j = arc4random() % (modulus--); 
    [selectedObjects addObject:[selectionPool objectAtIndex:j]]; 
    [selectionPool removeObjectAtIndex:j]; 

} 

No estoy seguro de qué tan eficiente que sería para grandes colecciones, pero ha funcionado para mí con colecciones que se encuentran en los cientos de objetos.

+0

obtendría división por cero excepción si 'numberOfObjectsToSelect == selectionPool.count' –

Cuestiones relacionadas