Вибір випадкового об'єкта в NSArray


83

Скажімо, у мене є масив із об’єктами, 1, 2, 3 та 4. Як вибрати випадковий об’єкт із цього масиву?


Усі відповіді тут правильні, але для більш сучасного рішення див. Мою відповідь тут . Він використовує arc4random_uniformметод, щоб уникнути зміщення за модулем.
Адам

не відповідь на це питання, а цікавий момент - інші колекції Foundation (NSSet NSHashTable) мають методи "anyObject", які зчитують довільний (випадковий) об'єкт із Set / HashTable. Можна застосувати цей метод у розширенні до NSArray, дотримуючись наведених нижче пропозицій.
Motti Shneor

Відповіді:


192

Відповідь @ Дарріла правильна, але міг би використати деякі незначні налаштування:

NSUInteger randomIndex = arc4random() % theArray.count;

Модифікації:

  • Використовувати arc4random()over rand()і random()простіше, оскільки не вимагає засівання (дзвінка srand()або srandom()).
  • Оператор modulo ( %) робить загальний вираз коротшим, а також робить його семантично чіткішим.

27
Зі сторінки man arc4random: arc4random_uniform () рекомендується для таких конструкцій, як `` arc4random ()% upper_bound '', оскільки це дозволяє уникнути "зміщення за модулем", коли верхня межа не є рівним двом.
Макс Янков

@collibhoy немає, тому що 0 % 4 = 0, 1 % 4 = 1, 2 % 4 = 2, 3 % 4 = 3, 4 % 4 = 0, 5 % 4 = 1... Якщо ви по модулю по п , ваш найбільший результат ніколи не буде більше , ніж п-1 .
Дейв Делонг

1
@DaveDeLong Відповідно до вихідного коду, countє властивістю:@interface NSArray<__covariant ObjectType> : NSObject <NSCopying, NSMutableCopying, NSSecureCoding, NSFastEnumeration> @property (readonly) NSUInteger count;
mikeho

17

Це найпростіше рішення, яке я міг придумати:

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

Необхідно перевірити , countтак як не- nilале спустошити NSArrayповернеться 0до count, і arc4random_uniform(0)повертається 0. Тож без перевірки ви вийдете за межі масиву.

Це рішення спокусливе, але неправильне, оскільки спричинить збій з порожнім масивом:

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

Для довідки, ось документація :

u_int32_t
arc4random_uniform(u_int32_t upper_bound);

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

Сторінка не згадує, що arc4random_uniformповертається, 0коли 0передається як upper_bound.

Крім того, arc4random_uniformце визначено в <stdlib.h>, але додавання #importнеобов’язкового в моїй тестовій програмі iOS.


11

Можливо, щось на зразок:

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

Не забудьте ініціалізувати генератор випадкових чисел (наприклад, srandomdev ()).

ПРИМІТКА. Відповідно до наведеної нижче відповіді я оновив для використання -count замість синтаксису крапок.


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

Редагувати: оновлено для Xcode 7. Загальні засоби, що дозволяють онулювати


1

Створіть випадкове число і використовуйте його як індекс. Приклад:

#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, якщо ви хочете трохи детальніше, ви можете скористатися SecRandomCopyBytes()для отримання криптографічно корисних випадкових чисел на iPhone у будь-якому випадку. На Mac ви маєте прямий доступ до / dev / random.

Я думаю, що головний сенс питання - показати, як вибрати випадковий елемент із масиву, і ця відповідь насправді не дає найкращої інформації.
beakr

Цей жарт мені дуже подобається, але я проголосував за допомогу тим, хто його не розуміє.
Stig Brautaset

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

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

inx - випадкове число.

де srand () може бути де завгодно в програмі до функції випадкового вибору.


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

якщо ви хочете передати це на int, ось рішення для цього (корисно, коли вам потрібен випадковий int з масиву непослідовних чисел, у випадку рандомізації виклику перерахування тощо)

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

0

У Swift 4:

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

array[Int(randomNumber)]

1
Будь ласка, перегляньте Як написати хорошу відповідь . Відповіді лише на коди не рекомендується, оскільки вони не пояснюють, як вони вирішують проблему у питанні. Вам слід оновити свою відповідь, щоб пояснити, що це робить і як покращує багато відповідей на це 7-річне запитання
FluffyKitten
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.