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?


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.

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.


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.


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


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

@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.


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];

0

In Swift 4:

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

array[Int(randomNumber)]

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
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.