2009-04-26 10 views
38

Esiste un modo canonico per randomizzare una matrice nell'obiettivo C?modo canonico per randomizzare un NSArray nell'obiettivo C

+1

possibile duplicato del [Qual è il modo migliore per Shuffle un NSMutableArray?] (Http: // StackOverflow .com/questions/56648/whats-the-best-way-to-shuffle-an-nsmutablearray) – Senseful

+1

La raccomandazione è [Fisher-Yates] (https://en.wikipedia.org/wiki/Fisher%E2%80% 93Yates_shuffle) per NSMutableArray: 'for (NSUInteger i = self.count; i> 1; i--) [self exchangeObjectAtIndex: i-1 con ObjectAtIndex: arc4random_uniform ((u_int32_t) i)]; ' –

risposta

59

La mia libreria di utilità definisce questa categoria su NSMutableArray per farlo:

@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's 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's 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 

Assicurarsi di inizializzare il generatore di numeri casuali (per esempio con srandom(time(NULL))) qualche tempo prima si chiama; altrimenti l'output non sarà molto casuale.

+0

Love it! Grazie per la condivisione. Sono estremamente curioso del resto della tua libreria di utilità ora. – PEZ

+0

La maggior parte non è algoritmica - è tutto da + [NSArray arrayWithCount: numbers:] (dove i "numeri" sono doppi) a - [UIView moveToSuperview: withFrame: animated:], oltre a uno stack di Core Data a singolo oggetto per kick . –

+0

Questo non funziona per me. Intendo che mescola "un po '" ma alcune parti sono ancora in ordine. Dato anche lo stesso array, il primo shuffle ha sempre lo stesso risultato. Quindi non è davvero casuale. –

2

Non c'è nessuno incorporato nell'SDK se è quello che stai chiedendo.

È possibile utilizzare praticamente qualsiasi algoritmo di randomizzazione o shuffling desiderato. Diversi algoritmi hanno diversi compromessi in termini di casualità, efficienza, ecc

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

Per algoritmi che CASUALE "in-place" inizia con una matrice mutevole utilizzare

insertObject:atIndex: 
removeObjectAtIndex: 

Per algoritmi che ricostruiscono il array, alimentarlo come originale e creare un nuovo array.

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

Assicurarsi di seminare la funzione random() sia con srandomdev() o srandom().

+0

Quello che hai scritto non ha senso come metodo di istanza. Poiché il codice esiste, dovrebbe essere solo una funzione o un metodo di classe. Idiomaticamente, dovrebbe essere un metodo di istanza NSArray che restituisce un nuovo array shuffled o un metodo di istanza NSMutableArray che si mescola automaticamente. – Chuck

+0

Vero, stavo solo dimostrando il codice qui, non ero davvero preoccupato di inserirlo in una categoria. Ho anche dimenticato di menzionare la funzione random() con srandomdev() o srandom(). –

+2

Questo codice è leggermente sbilanciato a causa del modulo; vedi http://en.wikipedia.org/wiki/Fisher-Yates_shuffle#Modulo_bias per ulteriori informazioni al riguardo. –

0

non c'è un modo canonico senza fare una categoria a NSArray (vale a dire hanno un metodo di istanza come arrayWithRandomizedIndices) o NSMutableArray (vale a dire hanno un metodo come randomizeIndices).

Ecco un esempio dalla mia libreria, parte di una categoria su NSMutableArray. Riorganizzerà casualmente l'array, piuttosto che rimescolare alcune voci.

- (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]; 
} 

chiamata srand(time(0)); o qualcosa del genere prima di chiamare questo metodo o nelle prime fasi del metodo.

1

La mia soluzione è un metodo di categoria che restituisce una copia dell'array (autoreleased) con elementi randomizzati (usando 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 
20

Eccolo!

- (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]; 
} 
0

NSArray randomizzazione come categoria Objective-C Metodo:

@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 

Come si è visto: NSArray Randomization & Psychedelia

Problemi correlati