NSSet как извлечь объект случайным образом?

Я не уверен в том, как работает NSSet anyObject. Что означает, что «возвращаемый объект выбирается для удобства набора» (из ссылка на класс NSSet) ?

Кроме того, как мне лучше всего случайным образом извлекать объекты из NSSet? Я думал о том, чтобы получить allObjects в массиве, а затем myArray[arc4random_uniform(x)], где x — количество объектов в массиве.


person ticofab    schedule 27.06.2012    source источник
comment
Вы согласны с повторами?   -  person Richard    schedule 28.06.2012
comment
Самому любопытно, я думаю, вы могли бы вызывать anyObject произвольное количество раз, но ваше решение с массивом звучит лучше.   -  person Patrick Borkowicz    schedule 28.06.2012
comment
Я предполагаю, что преобразование NSArray в NSSet туда и обратно не будет хорошим способом перетасовки.   -  person user4951    schedule 20.11.2012


Ответы (4)


Обычно экземпляры NSSet создаются с поддержкой CFHash, поэтому они почти всегда возвращают первый объект в этом хэше, так как поиск выполняется быстрее всего. Причина, по которой он говорит

Возвращаемый объект выбирается так, как удобно набору — не гарантируется, что выбор будет случайным.

Это потому, что вы не всегда знаете, что у него будет резервный массив. Насколько вам известно, экземпляр NSSet, который у вас есть, имеет поддержку NSDictionary или какую-то другую подобную структуру данных.

Итак, в заключение, если вам нужен случайный объект из NSSet, не используйте -anyObject, вместо этого используйте allObjects:, а затем перетасуйте этот массив.

person Richard J. Ross III    schedule 27.06.2012
comment
Да! Думаю, это ставит меня на первое место на сегодня! - person Richard J. Ross III; 28.06.2012
comment
На самом деле, NSSet создан с поддержкой CFBasicHash. Делать предположения, основанные на идее о том, что набор поддерживается массивом, так же небезопасно, как и делать предположения, основанные на идее о том, что он отсортирован случайным образом. - person Jonathan Grynspan; 28.06.2012
comment
См. CFSet.c для реализации NSSet (конкретные подклассы которого эквивалентно CFSet экземплярам.) - person Jonathan Grynspan; 28.06.2012


В документации написано, что anyObject возвращает

Один из объектов набора или nil, если набор не содержит объектов. Возвращаемый объект выбирается так, как удобно набору — не гарантируется, что выбор будет случайным.

Скорее всего, работает какой-то детерминированный алгоритм.

Наиболее надежным было бы, как вы предлагаете, создать NSArray с использованием метода NSSet allObjects, а затем выбрать случайный элемент из этого с arc4random() % N, где N - это count из NSArray.

person PengOne    schedule 27.06.2012
comment
Вместо этого лучше использовать arc4random_uniform, просто используя оператор по модулю, как предложил Фабио в своем вопросе, чтобы избежать смещения по модулю. - person Sven; 28.06.2012

Я использую arc4random() и два изменяемых массива, чтобы получить случайный и уникальный набор объектов:

NSMutableArray *selectionPool = ...;

int numberOfObjectsToSelect = x;

NSMutableArray *selectedObjects = [[NSMutableArray alloc] initWithCapacity:numberOfObjectsToSelect];

int modulus = selectionPool.count - 1;

for (int i = 0; i < numberOfObjectsToSelect; i++) {

    int j = arc4random() % (modulus--);
    [selectedObjects addObject:[selectionPool objectAtIndex:j]];
    [selectionPool removeObjectAtIndex:j];

}

Я не уверен, насколько эффективно это было бы для больших коллекций, но у меня это сработало с коллекциями, количество которых не превышает 100 объектов.

person jay492355    schedule 15.02.2013
comment
получит исключение деления на ноль, если numberOfObjectsToSelect == selectionPool.count - person Reza Shayestehpour; 29.09.2014