Канонический способ рандомизации NSArray в Objective C

Существует ли канонический способ рандомизации массива в Objective C?

Ответ 1

Моя библиотека утилиты определяет эту категорию в NSMutableArray для этого:

@interface NSMutableArray (ArchUtils_Shuffle)
- (void)shuffle;
@end

// Chooses a random integer below n without bias.
// Computes m, a power of two slightly above n, and takes random() modulo m,
// then throws away the random number if it between n and m.
// (More naive techniques, like taking random() modulo n, introduce a bias 
// towards smaller numbers in the range.)
static NSUInteger random_below(NSUInteger n) {
    NSUInteger m = 1;

    // Compute smallest power of two greater than n.
    // There probably a faster solution than this loop, but bit-twiddling
    // isn't my specialty.
    do {
        m <<= 1;
    } while(m < n);

    NSUInteger ret;

    do {
        ret = random() % m;
    } while(ret >= n);

    return ret;
}

@implementation NSMutableArray (ArchUtils_Shuffle)

- (void)shuffle {
    // http://en.wikipedia.org/wiki/Knuth_shuffle

    for(NSUInteger i = [self count]; i > 1; i--) {
        NSUInteger j = random_below(i);
        [self exchangeObjectAtIndex:i-1 withObjectAtIndex:j];
    }
}

@end

Перед тем, как позвонить, убедитесь, что вы запустили генератор случайных чисел (например, srandom(time(NULL))); в противном случае вывод будет не очень случайным.

Ответ 2

Вот оно!

- (NSArray*)shuffleArray:(NSArray*)array {

    NSMutableArray *temp = [[NSMutableArray alloc] initWithArray:array];

    for(NSUInteger i = [array count]; i > 1; i--) {
        NSUInteger j = arc4random_uniform(i);
        [temp exchangeObjectAtIndex:i-1 withObjectAtIndex:j];
    }

    return [NSArray arrayWithArray:temp];
}

Ответ 3

if ([array count] > 1) {
    for (NSUInteger shuffleIndex = [array count] - 1; shuffleIndex > 0; shuffleIndex--)
        [array exchangeObjectAtIndex:shuffleIndex withObjectAtIndex:random() % (shuffleIndex + 1)];
}

Обязательно запустите функцию random() с помощью srandomdev() или srandom().

Ответ 4

В SDK нет встроенного SDK, если это то, о чем вы просите.

Вы можете использовать практически любой алгоритм рандомизации или перетасовки, который вы хотите. Различные алгоритмы имеют разные компромиссы с точки зрения случайности, эффективности и т.д.

http://en.wikipedia.org/wiki/Shuffling#Shuffling_algorithms

Для алгоритмов, которые перетасовывают "на месте", начинаются с изменяемого массива, используйте

insertObject:atIndex:
removeObjectAtIndex:

Для алгоритмов, которые восстанавливают массив, передайте его оригиналу и создайте новый массив.

Ответ 5

Мое решение - это метод категории, который возвращает копию массива (autoreleased) с рандомизированными элементами (используя arc4random).

@interface NSArray (CMRandomised)

/* Returns a copy of the array with elements re-ordered randomly */
- (NSArray *)randomised;

@end

/* Returns a random integer number between low and high inclusive */
static inline int randomInt(int low, int high)
{
    return (arc4random() % (high-low+1)) + low;
}

@implementation NSArray (CMRandomised)

- (NSArray *)randomised
{
    NSMutableArray *randomised = [NSMutableArray arrayWithCapacity:[self count]];

    for (id object in self) {
        NSUInteger index = randomInt(0, [randomised count]);
        [randomised insertObject:object atIndex:index];
    }
    return randomised;
}

@end

Ответ 6

Нет канонического способа без создания категории на NSArray (т.е. иметь метод экземпляра типа arrayWithRandomizedIndices) или NSMutableArray (т.е. иметь такой метод, как randomizeIndices).

Вот пример из моей библиотеки, часть категории на NSMutableArray. Он будет случайным образом изменять порядок массива, а не перетасовывать несколько записей.

- (void) randomizeIndices
{
  if (self == nil || [self count] <= 1)
  {
    return;
  }

  int count = [self count];

  NSMutableArray* copySelf = [NSMutableArray arrayWithArray:self];
  NSMutableArray* mutableResultArray = [NSMutableArray alloc];
  mutableResultArray = [mutableResultArray initWithCapacity:count];
  [mutableResultArray autorelease];

  int objectsMovedCount = 0;

  for (int i = 0; i < count; i++)
  {
    int index = rand() % (count - objectsMovedCount);
    id anObject = [copySelf objectAtIndex:index];
    [mutableResultArray addObject:anObject];
    [copySelf removeObjectAtIndex:index];
    objectsMovedCount++;
  }
  [self setArray:mutableResultArray];
}

Вызов srand(time(0)); или некоторые из них перед вызовом этого метода или в начале метода.

Ответ 7

Рандомизация NSArray как метод Objective-C:

@implementation NSArray (NGDataDynamics)

- (NSArray *)jumbled
{
  NSMutableArray *jumbled = self.mutableCopy;

  NSUInteger idx = self.count-1;
  while(idx)
  {
    [jumbled exchangeObjectAtIndex:idx
                 withObjectAtIndex:arc4random_uniform(idx)];
    idx--;
  }

  return jumbled;
}

@end

Как видно: NSArray Randomization и Psychedelia