2016-01-12 5 views
1

Мне нужно выбрать случайный индекс из числа, включенного в NSIndexSet.Случайный индекс от NSIndexSet

Для справки, NSSet определяет -anyObject метод ( documentation) для выбора произвольных объектов из набора. Есть ли аналогичная функциональность в NSIndexSet? (Получается -anyObject не гарантирует возврат случайного объекта из набора.)

Если нет, то как он может быть реализован?

Примечание: Я нашел реализацию here, но она включает итерацию по элементам индекса. В идеале я бы хотел избежать перечисления.

Редактировать: К моему разочарованию, документация NSSet утверждает, что -anyObject не гарантируется возвращать случайный объект из набора. К сожалению, тот же вывод можно сделать из NSIndexSet documentation, regarding the implementation of -getIndexes:maxCount:inIndexRange:

+0

Я не вижу другого способа получения значений из индекса, отличного от перечисления. – trojanfoe

+0

Очень жаль. Я надеялся, что NSIndexSet, выполнив концептуально сходную роль с другими наборами базовых наборов, включит такую ​​базовую функциональность. – insys

+0

На самом деле есть 'getIndexes: maxCount: inIndexRange:', но это тоже может быть дорого, если только вы не захотите его кэшировать. – trojanfoe

ответ

0

Сначала создайте случайное число между 0 и [indexSet count]-1.

Теперь получите индекс в randomNum-м индексе от indexSet. Там нет indexAtIndex: метода как таковые, но этот код даст вам подобный результат:

NSUInteger index = [indexSet firstIndex]; 

for (NSUInteger i = 0, target = randomNum; i < target; i++) 
    index = [indexSet indexGreaterThanIndex:index]; 

также посмотреть на this question.

0

без перечисления

вы можете сделать что-то на более низкий уровне распределения/освобождая некоторую память вручную для индексов без перечисления, который представляет индекс случайного, а также:

NSIndexSet *_set = ... // your input index set 

NSUInteger *_integerCArray = malloc(_set.count * sizeof(NSUInteger)); 
#if __LP64__ 
    NSRange _indicesRange = NSMakeRange(0, UINT64_MAX); 
#else 
    NSRange _indicesRange = NSMakeRange(0, UINT32_MAX); 
#endif 
[_set getIndexes:_integerCArray maxCount:_set.count inIndexRange:&_indicesRange]; 
NSInteger _randomIndex = _integerArray[arc4random_uniform((u_int32_t)_set.count)]; // the random index 
free(_integerCArray), _integerCArray = nil; 

с перечислением

Я знаю, что вы сказали, что вас не интересуют перечисления, и, честно говоря, это не очень эффективный способ, но и, безусловно, дает вам случайный индекс, как вам нужно, и гораздо лучше читать и управление памятью гораздо безопаснее в этом случае:

NSIndexSet *_set = ... // your input index set 

__block NSInteger _counter = arc4random_uniform((u_int32_t)_set.count); // assume there are fewer indices in the set than UINT32_MAX 
NSInteger _randomIndex = [_set indexPassingTest:^BOOL(NSUInteger idx, BOOL * _Nonnull stop) { 
    return --_counter < 0; 
}]; 

NOTE : эта идея также может быть оптимизирована для O(n/2), используя опцию NSEnumerationReverse, если случайный счетчик больше _set.count/2, но я не беспокоился о том, что в этом ответе, если набор в основном огромен, это может быть хорошей идеей, но с несколько сотен индексов, вам не составит труда использовать даже это неуклюжие решения.

Смежные вопросы