What's the Best Way to Shuffle an NSMutableArray?

后端 未结 12 1704
太阳男子
太阳男子 2020-11-21 11:53

If you have an NSMutableArray, how do you shuffle the elements randomly?

(I have my own answer for this, which is posted below, but I\'m new to Cocoa an

相关标签:
12条回答
  • 2020-11-21 12:03

    I solved this by adding a category to NSMutableArray.

    Edit: Removed unnecessary method thanks to answer by Ladd.

    Edit: Changed (arc4random() % nElements) to arc4random_uniform(nElements) thanks to answer by Gregory Goltsov and comments by miho and blahdiblah

    Edit: Loop improvement, thanks to comment by Ron

    Edit: Added check that array is not empty, thanks to comment by 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
    
    0 讨论(0)
  • 2020-11-21 12:07

    If you import GameplayKit, there is a shuffled API:

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

    let shuffledArray = array.shuffled()
    
    0 讨论(0)
  • 2020-11-21 12:09

    Edit: This is not correct. For reference purposes, I did not delete this post. See comments on the reason why this approach is not correct.

    Simple code here:

    - (NSArray *)shuffledArray:(NSArray *)array
    {
        return [array sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
            if (arc4random() % 2) {
                return NSOrderedAscending;
            } else {
                return NSOrderedDescending;
            }
        }];
    }
    
    0 讨论(0)
  • 2020-11-21 12:15

    You don't need the swapObjectAtIndex method. exchangeObjectAtIndex:withObjectAtIndex: already exists.

    0 讨论(0)
  • 2020-11-21 12:15

    A slightly improved and concise solution (compared to the top answers).

    The algorithm is the same and is described in literature as "Fisher-Yates 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 and 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 and 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: A more concise solution in Swift is possible from iOS10 using GameplayKit.

    Note: An algorithm for unstable shuffling (with all positions forced to change if count > 1) is also available

    0 讨论(0)
  • 2020-11-21 12:15
    NSUInteger randomIndex = arc4random() % [theArray count];
    
    0 讨论(0)
提交回复
热议问题