84 votes

Choisir un objet aléatoire dans un NSArray

Disons que j'ai un tableau avec des objets, 1, 2, 3 et 4. Comment puis-je choisir un objet au hasard dans ce tableau ?

0 votes

Toutes les réponses sont correctes, mais pour une solution plus récente, voir ma réponse. ici . Il utilise le arc4random_uniform pour éviter le biais modulo.

0 votes

Ce n'est pas une réponse à cette question, mais un point intéressant - d'autres collections de Foundation (NSSet NSHashTable) ont des méthodes "anyObject" qui lisent un objet arbitraire (aléatoire) à partir du Set/HashTable. On pourrait implémenter cette méthode dans une extension de NSArray, en suivant les suggestions ci-dessous.

193voto

Dave DeLong Points 156978

La réponse de @Darryl est correcte, mais elle pourrait faire l'objet de quelques ajustements mineurs :

NSUInteger randomIndex = arc4random() % theArray.count;

Modifications :

  • Utilisation de arc4random() sur rand() y random() est plus simple car elle ne nécessite pas d'ensemencement (en appelant srand() o srandom() ).
  • El opérateur modulo ( % ) rend la déclaration globale plus courte, tout en la rendant sémantiquement plus claire.

27 votes

Extrait de la page de manuel arc4random : arc4random_uniform() est recommandé par rapport à des constructions comme ``arc4random() % upper_bound'' car il évite le "biais modulo" lorsque la limite supérieure n'est pas une puissance de deux.

0 votes

@collibhoy non, parce que 0 % 4 = 0 , 1 % 4 = 1 , 2 % 4 = 2 , 3 % 4 = 3 , 4 % 4 = 0 , 5 % 4 = 1 ... Si vous modulo par n votre résultat le plus important ne sera jamais supérieur à n-1 .

1 votes

@DaveDeLong Selon le code source, count est une propriété : @interface NSArray<__covariant ObjectType> : NSObject <NSCopying, NSMutableCopying, NSSecureCoding, NSFastEnumeration> @property (readonly) NSUInteger count;

17voto

funroll Points 4014

C'est la solution la plus simple que j'ai pu trouver :

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

Il est nécessaire de vérifier count car un non nil mais vide NSArray retournera 0 para count y arc4random_uniform(0) renvoie à 0 . Donc sans le contrôle, tu vas sortir des limites du tableau.

Cette solution est tentante mais elle est mauvais car cela provoquera un crash avec un tableau vide :

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

Pour référence, voici le 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 page de manuel ne mentionne pas que arc4random_uniform renvoie à 0 quand 0 est passé comme upper_bound .

Aussi, arc4random_uniform est défini dans <stdlib.h> mais en ajoutant le #import n'était pas nécessaire dans mon programme de test iOS.

11voto

Darryl H. Thomas Points 801

Peut-être quelque chose du genre :

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

N'oubliez pas d'initialiser le générateur de nombres aléatoires (srandomdev(), par exemple).

NOTE : J'ai mis à jour pour utiliser -count au lieu de la syntaxe point, selon la réponse ci-dessous.

9voto

Diejmon Points 111
@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

Edit : Mis à jour pour Xcode 7. Génériques, nullité

1voto

Générer un nombre aléatoire et l'utiliser comme index. Exemple :

#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;
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X