Frage

Wenn Sie haben ein NSMutableArray, wie Sie schieben Sie die Elemente nach dem Zufallsprinzip?

(Ich habe meine eigene Antwort darauf, die ist unten geschrieben, aber ich bin neu in Cocoa, und ich bin daran interessiert zu wissen, ob es einen besseren Weg gibt.)


Update:Wie bereits von @Mukesh, ab iOS 10+ und macOS 10.12+, es ist ein -[NSMutableArray shuffledArray] Methode, die verwendet werden können, zu mischen.Finden https://developer.apple.com/documentation/foundation/nsarray/1640855-shuffledarray?language=objc für die details.(Beachten Sie jedoch, dass dies erstellt eine neue array, anstatt das mischen der Elemente.)

War es hilfreich?

Lösung

Sie brauchen nicht die swapObjectAtIndex Methode. exchangeObjectAtIndex: withObjectAtIndex:. ist bereits vorhanden

Andere Tipps

Ich löste dies durch eine Kategorie NSMutableArray hinzuzufügen.

Edit:. entfernt unnötiges Verfahren dank von Ladd beantworten

Edit: Changed (arc4random() % nElements) dank arc4random_uniform(nElements) von Gregory Goltsov und Kommentare werden von Miho und blahdiblah beantworten

Edit: Loop-Verbesserung, dank von Ron Kommentar

Edit: wurde hinzugefügt, dass Array überprüfen nicht leer ist, dank von Mahesh Agrawal Kommentar

//  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

Da ich noch nicht kommentieren, ich dachte, dass ich eine sehr gute Reaktion beitragen würde. Ich veränderten Kristopher Johnson Implementierung für mein Projekt in einer Reihe von Möglichkeiten (wirklich versuchen, es so kurz wie möglich zu machen), ist einer von ihnen arc4random_uniform(), weil es vermeidet Modulo-Bias .

// 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

Von iOS 10 Sie den neuen shuffled API verwenden können:

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

let shuffledArray = array.shuffled()

Eine leicht verbesserte und präzise Lösung (im Vergleich zu den Top-Antworten).

Der Algorithmus ist der gleiche und wird in der Literatur als „ Fisher-Yates beschrieben Shuffle “.

In 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

In Swift 3.2 und 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))))
        }
    }
}

In Swift 3.0 und 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])
        }
    }
}

Hinweis: Eine knappe Lösung in Swift ist möglich, von iOS10 GameplayKit mit

.

Hinweis: Ein Algorithmus für instabile schlurfenden (mit allen Positionen ändern gezwungen, wenn count> 1) ist ebenfalls verfügbar

Dies ist die einfachste und schnellste Weg, NSArrays oder NSMutableArrays mischen (Objekt-Puzzles ist ein NSMutableArray, es enthält Puzzle-Objekte. Ich habe hinzugefügt Puzzle Objektvariable Index, der in Array-Anfangsposition angibt)

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++;
    }
}

Log-Ausgabe:

 #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

Sie auch mit obj2 vergleichen obj1 und entscheiden, was Sie wollen zurückkehren Mögliche Werte sind:

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

Es gibt eine schöne populäre Bibliothek, die diese Methode hat, wie es ein Teil ist, genannt SSToolKit in GitHub . Die Datei NSMutableArray + SSToolkitAdditions.h enthält Shuffle-Methode. Sie können es auch nutzen. Unter diesen scheint es jede Menge nützliche Dinge.

Die Hauptseite dieser Bibliothek ist hier .

Wenn Sie diese verwenden, der Code wie folgt sein:

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

Diese Bibliothek hat auch eine Pod (siehe CocoaPods)

Von iOS 10, können Sie NSArray shuffled() von GameplayKit . Hier ist ein Helfer für Array in 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())
    }
}

Wenn Elemente wiederholt haben.

z. Array: A A A B B oder B B A A A

einzige Lösung ist: A B A B A

sequenceSelected ist ein NSMutableArray, die Elemente der Klasse obj speichert, die Zeiger auf irgendeiner Reihenfolge sind.

- (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 Antwort ist recht nett, aber es ist nicht völlig zufällig.

ein Array von 2 Elementen gegeben, diese Funktion gibt immer die inversed Array, weil Sie die Reichweite Ihres zufällig über den Rest der Indizes generieren. Eine genauere shuffle() Funktion wäre wie

- (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:. Das ist nicht richtig Für Referenzzwecke, habe ich diesen Beitrag nicht löschen. Siehe Kommentare auf dem Grunde, warum dieser Ansatz nicht korrekt ist.

Einfacher Code hier:

- (NSArray *)shuffledArray:(NSArray *)array
{
    return [array sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
        if (arc4random() % 2) {
            return NSOrderedAscending;
        } else {
            return NSOrderedDescending;
        }
    }];
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top