Question

Si vous avez un NSMutableArray, comment vous mélangez les éléments de façon aléatoire?

(J'ai ma propre réponse à ce qui est affiché ci-dessous, mais je suis nouveau sur le Cacao et je suis intéressé de savoir si il ya une meilleure façon.)


Mise à jour:Comme le fait remarquer @Mukesh, comme d'iOS 10+ et macOS 10.12+, il y a un -[NSMutableArray shuffledArray] méthode qui peut être utilisée pour shuffle.Voir https://developer.apple.com/documentation/foundation/nsarray/1640855-shuffledarray?language=objc pour plus de détails.(Mais notez que cela crée un nouveau tableau, plutôt que de brouiller les éléments en place.)

Était-ce utile?

La solution

Vous n'avez pas besoin de la swapObjectAtIndex méthode. exchangeObjectAtIndex:withObjectAtIndex: il existe déjà.

Autres conseils

J'ai résolu ce problème en ajoutant une catégorie à NSMutableArray.

Edit: Retiré inutile méthode merci de répondre par Ladd.

Edit: Changé (arc4random() % nElements) pour arc4random_uniform(nElements) merci de répondre par Grégoire Goltsov et commentaires par miho et blahdiblah

Edit: Boucle d'amélioration, grâce aux commentaires de Ron

Edit: Ajout d'une vérification de ce tableau n'est pas vide, grâce à un commentaire par Mahesh Agrawal

//  NSMutableArray_Shuffling.h

#if TARGET_OS_IPHONE
#import <UIKit/UIKit.h>
#else
#include <Cocoa/Cocoa.h>
#endif

// This category enhances NSMutableArray by providing
// methods to randomly shuffle the elements.
@interface NSMutableArray (Shuffling)
- (void)shuffle;
@end


//  NSMutableArray_Shuffling.m

#import "NSMutableArray_Shuffling.h"

@implementation NSMutableArray (Shuffling)

- (void)shuffle
{
    NSUInteger count = [self count];
    if (count <= 1) return;
    for (NSUInteger i = 0; i < count - 1; ++i) {
        NSInteger remainingCount = count - i;
        NSInteger exchangeIndex = i + arc4random_uniform((u_int32_t )remainingCount);
        [self exchangeObjectAtIndex:i withObjectAtIndex:exchangeIndex];
    }
}

@end

Puisque je ne peux pas encore de commentaire, j'ai pensé que je pourrais contribuer à une réponse complète.J'ai modifié Kristopher Johnson de la mise en œuvre de mon projet dans un certain nombre de moyens (vraiment essayer de le rendre aussi précis que possible), l'un d'eux étant arc4random_uniform() parce qu'il évite modulo biais.

// NSMutableArray+Shuffling.h
#import <Foundation/Foundation.h>

/** This category enhances NSMutableArray by providing methods to randomly
 * shuffle the elements using the Fisher-Yates algorithm.
 */
@interface NSMutableArray (Shuffling)
- (void)shuffle;
@end

// NSMutableArray+Shuffling.m
#import "NSMutableArray+Shuffling.h"

@implementation NSMutableArray (Shuffling)

- (void)shuffle
{
    NSUInteger count = [self count];
    for (uint i = 0; i < count - 1; ++i)
    {
        // Select a random element between i and end of array to swap with.
        int nElements = count - i;
        int n = arc4random_uniform(nElements) + i;
        [self exchangeObjectAtIndex:i withObjectAtIndex:n];
    }
}

@end

De iOS 10, vous pouvez utiliser le nouveau shuffled API:

https://developer.apple.com/reference/foundation/nsarray/1640855-shuffled

let shuffledArray = array.shuffled()

Une légère amélioration et concis solution (en comparaison avec les réponses).

L'algorithme est le même et est décrite dans la littérature comme "Shuffle de Fisher-Yates".

En Objective-C:

@implementation NSMutableArray (Shuffle)
// Fisher-Yates shuffle
- (void)shuffle
{
    for (NSUInteger i = self.count; i > 1; i--)
        [self exchangeObjectAtIndex:i - 1 withObjectAtIndex:arc4random_uniform((u_int32_t)i)];
}
@end

Dans Swift 3.2 et 4.x:

extension Array {
    /// Fisher-Yates shuffle
    mutating func shuffle() {
        for i in stride(from: count - 1, to: 0, by: -1) {
            swapAt(i, Int(arc4random_uniform(UInt32(i + 1))))
        }
    }
}

Dans Swift 3.0 et 3.1:

extension Array {
    /// Fisher-Yates shuffle
    mutating func shuffle() {
        for i in stride(from: count - 1, to: 0, by: -1) {
            let j = Int(arc4random_uniform(UInt32(i + 1)))
            (self[i], self[j]) = (self[j], self[i])
        }
    }
}

Note: Plus concis solution rapide est possible à partir de iOS10 à l'aide de GameplayKit.

Note: Un algorithme pour unstable brassage (avec toutes les positions forcé à changer si count > 1) est également disponible

C'est le plus simple et le plus rapide pour shuffle NSArrays ou NSMutableArrays (objet puzzles est un NSMutableArray, il contient de puzzle objets.J'ai ajouté à puzzle variable d'objet index qui indique la position initiale dans la gamme)

int randomSort(id obj1, id obj2, void *context ) {
        // returns random number -1 0 1
    return (random()%3 - 1);    
}

- (void)shuffle {
        // call custom sort function
    [puzzles sortUsingFunction:randomSort context:nil];

    // show in log how is our array sorted
        int i = 0;
    for (Puzzle * puzzle in puzzles) {
        NSLog(@" #%d has index %d", i, puzzle.index);
        i++;
    }
}

la sortie du journal:

 #0 has index #6
 #1 has index #3
 #2 has index #9
 #3 has index #15
 #4 has index #8
 #5 has index #0
 #6 has index #1
 #7 has index #4
 #8 has index #7
 #9 has index #12
 #10 has index #14
 #11 has index #16
 #12 has index #17
 #13 has index #10
 #14 has index #11
 #15 has index #13
 #16 has index #5
 #17 has index #2

vous pouvez ainsi comparer obj1 avec obj2 et de décider ce que vous voulez revenir les valeurs possibles sont:

  • NSOrderedAscending = -1
  • NSOrderedSame = 0
  • NSOrderedDescending = 1

Il y a une belle bibliothèque populaire, qui a cette méthode il fait partie, appelée SSToolKit dans GitHub.Fichier NSMutableArray+SSToolkitAdditions.h contient shuffle méthode.Vous pouvez aussi l'utiliser.Parmi ceux-ci, il semble y avoir des tonnes de choses utiles.

La page principale de cette bibliothèque est ici.

Si vous utilisez cette option, votre code sera comme ceci:

#import <SSCategories.h>
NSMutableArray *tableData = [NSMutableArray arrayWithArray:[temp shuffledArray]];

Cette bibliothèque dispose également d'un Pod (voir CocoaPods)

De iOS 10, vous pouvez utiliser NSArray shuffled() de GameplayKit.Voici une aide pour Tableau dans Swift 3:

import GameplayKit

extension Array {
    @available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
    func shuffled() -> [Element] {
        return (self as NSArray).shuffled() as! [Element]
    }
    @available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
    mutating func shuffle() {
        replaceSubrange(0..<count, with: shuffled())
    }
}

Si des éléments ont répétitions.

par exempletableau:A A A B B ou B B A A A

seule solution, c'est:A B A B A

sequenceSelected est un NSMutableArray qui stocke les éléments de la classe obj, qui sont des pointeurs vers une certaine séquence.

- (void)shuffleSequenceSelected {
    [sequenceSelected shuffle];
    [self shuffleSequenceSelectedLoop];
}

- (void)shuffleSequenceSelectedLoop {
    NSUInteger count = sequenceSelected.count;
    for (NSUInteger i = 1; i < count-1; i++) {
        // Select a random element between i and end of array to swap with.
        NSInteger nElements = count - i;
        NSInteger n;
        if (i < count-2) { // i is between second  and second last element
            obj *A = [sequenceSelected objectAtIndex:i-1];
            obj *B = [sequenceSelected objectAtIndex:i];
            if (A == B) { // shuffle if current & previous same
                do {
                    n = arc4random_uniform(nElements) + i;
                    B = [sequenceSelected objectAtIndex:n];
                } while (A == B);
                [sequenceSelected exchangeObjectAtIndex:i withObjectAtIndex:n];
            }
        } else if (i == count-2) { // second last value to be shuffled with last value
            obj *A = [sequenceSelected objectAtIndex:i-1];// previous value
            obj *B = [sequenceSelected objectAtIndex:i]; // second last value
            obj *C = [sequenceSelected lastObject]; // last value
            if (A == B && B == C) {
                //reshufle
                sequenceSelected = [[[sequenceSelected reverseObjectEnumerator] allObjects] mutableCopy];
                [self shuffleSequenceSelectedLoop];
                return;
            }
            if (A == B) {
                if (B != C) {
                    [sequenceSelected exchangeObjectAtIndex:i withObjectAtIndex:count-1];
                } else {
                    // reshuffle
                    sequenceSelected = [[[sequenceSelected reverseObjectEnumerator] allObjects] mutableCopy];
                    [self shuffleSequenceSelectedLoop];
                    return;
                }
            }
        }
    }
}
NSUInteger randomIndex = arc4random() % [theArray count];

Kristopher Johnson répondre est assez sympa, mais ce n'est pas totalement aléatoire.

Étant donné un tableau de 2 éléments, cette fonction renvoie toujours l'inversion de la matrice, parce que vous êtes la génération de la gamme de votre hasard sur le reste de l'index.Plus de précision shuffle() la fonction serait comme

- (void)shuffle
{
   NSUInteger count = [self count];
   for (NSUInteger i = 0; i < count; ++i) {
       NSInteger exchangeIndex = arc4random_uniform(count);
       if (i != exchangeIndex) {
            [self exchangeObjectAtIndex:i withObjectAtIndex:exchangeIndex];
       }
   }
}

Edit: Ce n'est pas correct. À des fins de référence, je n'ai pas supprimer ce post.Voir les commentaires sur la raison pour laquelle cette approche n'est pas correcte.

Simple code ici:

- (NSArray *)shuffledArray:(NSArray *)array
{
    return [array sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
        if (arc4random() % 2) {
            return NSOrderedAscending;
        } else {
            return NSOrderedDescending;
        }
    }];
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top