2010-07-23 8 views
80

Angenommen, ich habe ein Array mit Objekten, 1, 2, 3 und 4. Wie würde ich ein zufälliges Objekt aus diesem Array auswählen?Wählen Sie ein zufälliges Objekt in einem NSArray

+0

Alle Antworten hier sind korrekt, aber für mehr aktuelle Lösung siehe meine Antwort [hier] (http://stackoverflow.com/a/10837462/730701). Es verwendet die 'arc4random_uniform'-Methode, um Modulo-Bias zu vermeiden. – Adam

Antwort

184

@ Darryls Antwort richtig ist, konnte aber einige kleinere Verbesserungen verwenden:

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

Modifikationen:

  • Mit arc4random() über rand() und random() ist einfacher, weil es nicht Impfen erfordert (Aufruf srand() oder srandom()).
  • Die modulo operator (%) macht die Gesamtaussage kürzer und macht sie auch semantisch klarer.
  • theArray.count ist falsch. Es funktioniert, aber count ist nicht als auf NSArray deklariert und sollte daher nicht über Punktsyntax aufgerufen werden. Dass es funktioniert, ist nur ein Nebeneffekt davon, wie die Punktsyntax vom Compiler interpretiert wird.
+2

Beachten Sie, dass RC4/ARC4 keine einheitliche Ausgabe liefert. –

+43

"theArray.count ist falsch. Es wird funktionieren, aber count wird nicht als @property auf NSArray deklariert und sollte daher nicht über Punktsyntax aufgerufen werden." --- Das ist nicht korrekt. Die Punktsyntax und die deklarierten Eigenschaften stehen nicht in Beziehung zueinander: Sie können die Punktsyntax für keine Argumentmethoden ohne Probleme verwenden. –

+27

Von arc4random man page: arc4random_uniform() wird über Konstruktionen wie "arc4random()% outer_bound" empfohlen, da es "modulo bias" vermeidet, wenn die obere Grenze keine Zweierpotenz ist. –

1

Generieren Sie eine Zufallszahl und verwenden Sie sie als Index. Beispiel:

#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

So einfach und doch funktioniert es so gut! – Joshua

+0

@Joshua Wenn Sie ein wenig mehr Details wünschen, können Sie 'SecRandomCopyBytes()' verwenden, um kryptografisch nützliche Zufallszahlen zu erhalten, auf dem iPhone sowieso. Auf Mac haben Sie direkten Zugriff auf/dev/random. –

+0

Eine Zufallszahl sollte genügen aber trotzdem danke für die Zusatzinformation. – Joshua

10

Vielleicht etwas entlang der Linien von:

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

Vergessen Sie nicht, den Zufallszahlengenerator (srandomdev(), zum Beispiel) zu initialisieren.

HINWEIS: Ich habe aktualisiert, um -count anstelle der Punktsyntax zu verwenden, wie unten beschrieben.

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

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

inx ist die Zufallszahl.

Dabei kann srand() irgendwo im Programm vor der Zufallsauswahlfunktion stehen.

16

Dies ist die einfachste Lösung, die ich tun konnte:

Es ist notwendig, count weil ein nicht nil aber leer NSArray kehrt 0 für count und arc4random_uniform(0) kehrt zu überprüfen 0

id object = array.count == 0 ? nil : array[arc4random_uniform(array.count)]; 
. Ohne die Überprüfung werden Sie auf dem Array außerhalb der Grenzen gehen.

Diese Lösung ist verlockend aber ist falsch, weil es zu einem Absturz mit einem leeren Array verursachen:

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

Als Referenz ist hier der documentation:

u_int32_t 
arc4random_uniform(u_int32_t upper_bound); 

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

Der Mann Seite doesn‘ Erwähnen Sie, dass arc4random_uniform0 zurückgibt, wenn 0 als upper_bound übergeben wird.

Auch arc4random_uniform ist in <stdlib.h> definiert, aber das Hinzufügen der #import war nicht notwendig in meinem iOS-Testprogramm.

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 

Edit: Aktualisiert für Xcode 7. Generics, NULL-Zulässigkeit

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

wenn man das in einen int werfen möchten, hier ist die Lösung für das (nützlich, wenn Sie einen zufälligen int von einem brauchen Anordnung von nicht-fortlaufenden Nummern im Fall einen ENUM Anruf Randomisierung, etc)

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

in Swift 4:

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

array[Int(randomNumber)] 
+0

Bitte überprüfen Sie [Wie schreibe ich eine gute Antwort] (https://stackoverflow.com/help/how-to-answer). Code-only-Antworten werden nicht empfohlen, da sie nicht erklären, wie sie das Problem in der Frage beheben. Sie sollten Ihre Antwort aktualisieren, um zu erklären, was dies bewirkt und wie es die vielen Antworten verbessert, die diese 7-jährige Frage bereits hat – FluffyKitten

Verwandte Themen