objective-c - lecturas - revista ethos
Escogiendo un objeto aleatorio en un NSArray (8)
En Swift 4:
let array = ["one","two","three","four"]
let randomNumber = arc4random_uniform(UInt32(array.count))
array[Int(randomNumber)]
Supongamos que tengo una matriz con objetos, 1, 2, 3 y 4. ¿Cómo podría elegir un objeto aleatorio de esta matriz?
Esta es la solución más simple que podría encontrar:
id object = array.count == 0 ? nil : array[arc4random_uniform(array.count)];
Es necesario verificar el count
porque un NSArray
no nil
pero vacío devolverá 0
para count
, y arc4random_uniform(0)
devolverá 0
. Entonces sin el cheque, saldrás de límites en la matriz.
Esta solución es tentadora, pero está mal porque causará un bloqueo con una matriz vacía:
id object = array[arc4random_uniform(array.count)];
Como referencia, aquí está la documentation :
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 man no menciona que arc4random_uniform
devuelve 0
cuando 0
se pasa como upper_bound
.
Además, arc4random_uniform
se define en <stdlib.h>
, pero no fue necesario agregar #import
en mi programa de prueba de iOS.
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;
}
La respuesta de @Darryl es correcta, pero podría usar algunos ajustes menores:
NSUInteger randomIndex = arc4random() % [theArray count];
Modificaciones:
- Usar
arc4random()
sobrerand()
yrandom()
es más simple porque no requiere siembra (llamando asrand()
osrandom()
). - El operador de módulo (
%
) hace que la instrucción global sea más corta, a la vez que la vuelve semánticamente más clara. -
theArray.count
está mal. Funcionará, pero elcount
no se declara como@property
enNSArray
y, por lo tanto, no se debe invocar mediante la sintaxis de punto. Que funcione es simplemente un efecto secundario de cómo el compilador interpreta la sintaxis de punto.
Tal vez algo como:
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 sintaxis de punto, según la respuesta a continuación.
srand([[NSDate date] timeIntervalSince1970]);
int inx =rand()%[array count];
inx es el número aleatorio.
donde srand () puede estar en cualquier parte del programa antes de la función de selección aleatoria.
@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. Generics, Nullability
ObjectType *objectVarName = [array objectAtIndex:arc4random_uniform((int)(array.count - 1))];
si quieres convertirlo en un int, aquí está la solución para eso (útil para cuando necesitas 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];