Wybór losowego obiektu w tablicy NSArray


83

Powiedzmy, że mam tablicę z obiektami 1, 2, 3 i 4. Jak wybrać losowy obiekt z tej tablicy?


Wszystkie odpowiedzi tutaj są poprawne, ale aby uzyskać bardziej aktualne rozwiązanie, zobacz moją odpowiedź tutaj . Używa arc4random_uniformmetody, aby uniknąć odchylenia modulo.
Adam

nie jest odpowiedzią na to pytanie, ale ciekawostka - inne kolekcje Foundation (NSSet NSHashTable) mają metody „anyObject”, które odczytują dowolny (losowy) obiekt z tabeli Set / HashTable. Można zaimplementować tę metodę w rozszerzeniu NSArray, postępując zgodnie z poniższymi sugestiami.
Motti Shneor

Odpowiedzi:


192

Odpowiedź @ Darryl jest poprawna, ale przydałoby się kilka drobnych poprawek:

NSUInteger randomIndex = arc4random() % theArray.count;

Modyfikacje:

  • Używanie arc4random()over rand()i random()jest prostsze, ponieważ nie wymaga inicjowania (wywoływania srand()lub srandom()).
  • Operator modulo ( %) sprawia, że ogólny rachunek krótsze, a jednocześnie czyni go semantycznie jaśniejsze.

27
Ze strony podręcznika arc4random: arc4random_uniform () jest zalecana zamiast konstrukcji takich jak `` arc4random ()% upper_bound '', ponieważ pozwala uniknąć "odchylenia modulo", gdy górna granica nie jest potęgą dwóch.
Max Yankov

@collibhoy nie, dlatego 0 % 4 = 0, 1 % 4 = 1, 2 % 4 = 2, 3 % 4 = 3, 4 % 4 = 0, 5 % 4 = 1... Jeśli modulo przez n , twój największy wynik nigdy nie będzie większa niż n-1 .
Dave DeLong

1
@DaveDeLong Zgodnie z kodem źródłowym countjest to właściwość:@interface NSArray<__covariant ObjectType> : NSObject <NSCopying, NSMutableCopying, NSSecureCoding, NSFastEnumeration> @property (readonly) NSUInteger count;
mikeho

17

Oto najprostsze rozwiązanie, jakie mogłem wymyślić:

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

Konieczne jest sprawdzenie, countponieważ nilniepusty, ale NSArraywróci 0do counti arc4random_uniform(0)wróci 0. Więc bez sprawdzenia wyjdziesz poza granice tablicy.

To rozwiązanie jest kuszące, ale jest błędne, ponieważ spowoduje awarię z pustą tablicą:

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

W celach informacyjnych, oto dokumentacja :

u_int32_t
arc4random_uniform(u_int32_t upper_bound);

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

Strona podręcznika nie wspomina, że arc4random_uniformzwraca, 0gdy 0jest przekazywana jako upper_bound.

Jest również arc4random_uniformzdefiniowany w programie <stdlib.h>, ale dodanie #importnie było konieczne w moim programie testowym na iOS.


11

Być może coś w rodzaju:

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

Nie zapomnij zainicjować generatora liczb losowych (na przykład srandomdev ()).

UWAGA: Zaktualizowałem, aby używać -count zamiast składni kropek, zgodnie z odpowiedzią poniżej.


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

Edycja: zaktualizowano dla Xcode 7. Generics, nullability


1

Wygeneruj liczbę losową i użyj jej jako indeksu. Przykład:

#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, jeśli chcesz uzyskać trochę więcej szczegółów, możesz użyć, SecRandomCopyBytes()aby uzyskać przydatne kryptograficznie liczby losowe, i tak na iPhonie. Na Macu masz bezpośredni dostęp do / dev / random.

Myślę, że głównym celem pytania jest pokazanie, jak wybrać losowy element z tablicy, a ta odpowiedź tak naprawdę nie daje najlepszych informacji.
beakr

Bardzo podoba mi się ten żart, ale przegłosowałem go, aby pomóc tym, którzy go nie rozumieją.
Stig Brautaset

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

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

inx to liczba losowa.

gdzie srand () może znajdować się w dowolnym miejscu programu przed funkcją losowego wybierania.


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

jeśli chcesz rzutować to na int, oto rozwiązanie tego problemu (przydatne, gdy potrzebujesz losowej liczby int z tablicy liczb niesekwencyjnych, w przypadku losowego wywołania wyliczenia itp.)

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

0

W Swift 4:

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

array[Int(randomNumber)]

1
Przejrzyj Jak napisać dobrą odpowiedź . Odpowiedzi zawierające tylko kod nie są zalecane, ponieważ nie wyjaśniają, w jaki sposób rozwiązują problem w pytaniu. Powinieneś zaktualizować swoją odpowiedź, aby wyjaśnić, co to robi i jak poprawia się w wielu odpowiedziach, które już ma to 7-letnie pytanie
FluffyKitten
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.