[objective-c] NSMutableArray를 섞는 가장 좋은 방법은 무엇입니까?

가있는 경우 NSMutableArray요소를 무작위로 임의 섞는 방법은 무엇입니까?

(아래에 게시 된 이것에 대한 내 대답이 있지만 Cocoa를 처음 사용하고 더 좋은 방법이 있는지 알고 싶습니다.)


업데이트 : @Mukesh가 지적한 것처럼 iOS 10 이상 및 macOS 10.12 이상에서는 -[NSMutableArray shuffledArray]셔플하는 데 사용할 수 있는 방법이 있습니다. 자세한 내용은 https://developer.apple.com/documentation/foundation/nsarray/1640855-shuffledarray?language=objc 를 참조하십시오. (그러나 이것은 요소를 제자리에 섞지 않고 새로운 배열을 만듭니다.)



답변

swapObjectAtIndex 메소드가 필요하지 않습니다. exchangeObjectAtIndex : withObjectAtIndex :가 이미 존재합니다.


답변

NSMutableArray에 범주를 추가하여이 문제를 해결했습니다.

편집 : Ladd의 답변 덕분에 불필요한 방법이 제거되었습니다.

편집 : Gregory Goltsov의 답변과 miho 및 blahdiblah의 의견 덕분에 변경 (arc4random() % nElements)되었습니다 .arc4random_uniform(nElements)

편집 : Ron의 의견 덕분에 루프 개선

편집 : 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


답변

아직 댓글을 달 수 없기 때문에 전체 답변을 제공 할 것이라고 생각했습니다. 필자는 프로젝트에 대한 Kristopher Johnson의 구현을 다양한 방법으로 (실제로 간결하게 만들려고 노력했습니다) 수정했습니다. 그 중 하나 arc4random_uniform()모듈로 편향을 피하기 때문 입니다.

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


답변

당신이 가져 오는 경우 GameplayKit하는이 shuffledAPI :

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

let shuffledArray = array.shuffled()


답변

약간 개선되고 간결한 솔루션 (상위 답변과 비교).

알고리즘은 동일하며 문헌에서 ” Fisher-Yates shuffle ” 로 설명되어 있습니다.

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

스위프트 3.2 및 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))))
        }
    }
}

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

참고 : iOS10에서는 Swift에서 더 간결한 솔루션을 사용할 수 GameplayKit있습니다.

참고 : 불안정한 셔플 링 알고리즘 (카운트> 1 인 경우 모든 위치를 강제로 변경)도 사용할 수 있습니다


답변

이것은 NSArrays 또는 NSMutableArrays를 섞는 가장 간단하고 빠른 방법입니다 (객체 퍼즐은 NSMutableArray이며 퍼즐 객체를 포함합니다. 배열의 초기 위치를 나타내는 퍼즐 객체 변수 색인에 추가했습니다)

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

로그 출력 :

 #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

obj1과 obj2를 비교하고 가능한 값을 반환 할 항목을 결정할 수 있습니다.

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

답변

GitHub의 SSToolKit 이라는 부분 으로이 방법을 사용하는 인기있는 라이브러리 가 있습니다 . 파일 NSMutableArray + SSToolkitAdditions.h는 셔플 방법을 포함합니다. 사용할 수도 있습니다. 이 중에서도 유용한 것들이 많이있는 것 같습니다.

이 라이브러리의 메인 페이지는 여기에 있습니다 .

이것을 사용하면 코드는 다음과 같습니다.

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

이 라이브러리에는 포드도 있습니다 (CocoaPods 참조).