Выбор случайного объекта в массиве NSArray

83

Скажем, у меня есть массив с объектами, 1, 2, 3 и 4. Как мне выбрать случайный объект из этого массива?

Джошуа
источник
Все ответы здесь верны, но для получения более свежего решения см. Мой ответ здесь . Он использует arc4random_uniformметод, чтобы избежать смещения по модулю.
Адам
не ответ на этот вопрос, но интересный момент - другие коллекции Foundation (NSSet NSHashTable) имеют методы anyObject, которые считывают произвольный (случайный) объект из Set / HashTable. Можно реализовать этот метод в расширении NSArray, следуя предложениям ниже.
Мотти Шнеор

Ответы:

192

@ Ответ Дэррила правильный, но можно было бы внести некоторые незначительные изменения:

NSUInteger randomIndex = arc4random() % theArray.count;

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

  • Использование arc4random()over rand()и random()проще, поскольку не требует заполнения (вызова srand()илиsrandom() ).
  • Оператор по модулю ( %) делает общий оператор короче, а также делает его семантически понятнее.
Дэйв Делонг
источник
27
Из справочной страницы 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 добавлять не было необходимости.

Funroll
источник
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. Обобщения, допустимость значений NULL

Александр Белявский
источник
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
Мне очень нравится эта шутка, но я проголосовал против нее, чтобы помочь тем, кто ее не понял.
Стиг Браутасет 01
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
Пожалуйста, просмотрите Как мне написать хороший ответ . Ответы только на код не приветствуются, потому что они не объясняют, как они решают проблему в вопросе. Вы должны обновить свой ответ, чтобы объяснить, что это делает и как он улучшает многие ответы на этот вопрос
семилетней давности