Auswählen eines zufälligen Objekts in einem NSArray

83

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

Joshua
quelle
Alle Antworten hier sind korrekt, aber für eine aktuellere Lösung siehe meine Antwort hier . Es verwendet die arc4random_uniformMethode, um Modulo Bias zu vermeiden.
Adam
Keine Antwort auf diese Frage, aber ein interessanter Punkt - andere Foundation-Sammlungen (NSSet NSHashTable) haben Methoden "anyObject", die ein beliebiges (zufälliges) Objekt aus der Set / HashTable lesen. Man könnte diese Methode in einer Erweiterung von NSArray implementieren, indem man den folgenden Vorschlägen folgt.
Motti Shneor

Antworten:

192

@ Darryls Antwort ist richtig, könnte aber einige kleinere Änderungen gebrauchen:

NSUInteger randomIndex = arc4random() % theArray.count;

Änderungen:

  • Die Verwendung von arc4random()over rand()und random()ist einfacher, da kein Seeding (Aufruf srand()oder srandom()) erforderlich ist .
  • Der Modulo-Operator ( %) verkürzt die Gesamtanweisung und macht sie gleichzeitig semantisch klarer.
Dave DeLong
quelle
27
Auf der Manpage von arc4random: arc4random_uniform () wird gegenüber Konstruktionen wie "arc4random ()% Upper_bound" empfohlen, da "Modulo Bias" vermieden wird, wenn die Obergrenze keine Zweierpotenz ist.
Max Yankov
@collibhoy nein, denn 0 % 4 = 0, 1 % 4 = 1, 2 % 4 = 2, 3 % 4 = 3, 4 % 4 = 0, 5 % 4 = 1... Falls Sie mit dem Modulo n , Ihr größten Ergebnis wird nie größer sein als n-1 .
Dave DeLong
1
@ DaveDeLong Laut Quellcode countist eine Eigenschaft:@interface NSArray<__covariant ObjectType> : NSObject <NSCopying, NSMutableCopying, NSSecureCoding, NSFastEnumeration> @property (readonly) NSUInteger count;
Mikeho
17

Dies ist die einfachste Lösung, die ich finden könnte:

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

Es ist zu prüfen , countweil ein nicht nilaber leer NSArrayzurückkehren wird 0für countund arc4random_uniform(0)kehrt 0. Ohne die Überprüfung werden Sie also die Grenzen des Arrays überschreiten.

Diese Lösung ist verlockend, aber falsch, da sie einen Absturz mit einem leeren Array verursacht:

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

Als Referenz finden Sie hier die Dokumentation :

u_int32_t
arc4random_uniform(u_int32_t upper_bound);

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

In der Manpage wird nicht erwähnt, dass arc4random_uniformzurückgegeben wird, 0wenn 0als übergeben wird upper_bound.

Auch arc4random_uniformist in definiert <stdlib.h>, aber das Hinzufügen #importwar in meinem iOS-Testprogramm nicht notwendig.

Funroll
quelle
11

Vielleicht etwas in der Art von:

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

Vergessen Sie nicht, den Zufallszahlengenerator (z. B. srandomdev ()) zu initialisieren.

HINWEIS: Ich habe gemäß der folgenden Antwort die Verwendung von -count anstelle der Punktsyntax aktualisiert.

Darryl H. Thomas
quelle
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

Bearbeiten: Aktualisiert für Xcode 7. Generika, Nullbarkeit

Alexander Belyavskiy
quelle
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;
}

quelle
@Joshua Wenn Sie ein wenig mehr Details wünschen, können Sie SecRandomCopyBytes()auf dem iPhone ohnehin kryptografisch nützliche Zufallszahlen abrufen . Auf dem Mac haben Sie direkten Zugriff auf / dev / random.
Ich denke, der Hauptpunkt der Frage ist zu zeigen, wie man ein zufälliges Element aus dem Array auswählt, und diese Antwort liefert nicht wirklich die besten Informationen.
Beakr
Ich mag diesen Witz sehr, aber ich habe ihn abgelehnt, um denen zu helfen, die ihn nicht verstehen.
Stig Brautaset
0
 srand([[NSDate date]  timeIntervalSince1970]);

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

Inx ist die Zufallszahl.

Dabei kann srand () an einer beliebigen Stelle im Programm vor der Zufallsauswahlfunktion stehen.

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

Wenn Sie dies in ein int umwandeln möchten, finden Sie hier die Lösung dafür (nützlich, wenn Sie ein zufälliges int aus einem Array nicht sequentieller Zahlen benötigen, wenn Sie einen Aufzählungsaufruf randomisieren usw.).

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

In Swift 4:

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

array[Int(randomNumber)]
Ankit garg
quelle
1
Bitte überprüfen Sie, wie ich eine gute Antwort schreibe . Von Nur-Code-Antworten wird abgeraten, da sie nicht erklären, wie sie das Problem in der Frage lösen. 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