NSMutable Array를 셔플하는 가장 좋은 방법은 무엇입니까?
를 가지고 있는 경우NSMutableArray
, 요소를 무작위로 섞는 방법은 무엇입니까?
(아래에 게재되어 있는 이 문제에 대한 저만의 답변이 있습니다만, 저는 코코아를 처음 접하는 사람입니다만, 더 좋은 방법이 있는지 알고 싶습니다.)
업데이트: @Mukesh에서 설명한 바와 같이 iOS 10+ 및 MacOS 10.12+에서는-[NSMutableArray shuffledArray]
셔플에 사용할 수 있는 방법.자세한 것은, https://developer.apple.com/documentation/foundation/nsarray/1640855-shuffledarray?language=objc 를 참조해 주세요(단, 새로운 어레이가 작성됩니다).
NSMutable Array에 카테고리를 추가하여 해결했습니다.
편집 : Ladd의 답변으로 불필요한 메서드를 삭제하였습니다.
편집: 변경됨(arc4random() % nElements)
로.arc4random_uniform(nElements)
Gregory Goltsov의 답변과 miho와 blahdiblah의 코멘트 덕분에
편집: 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
swap Object At는 필요 없습니다.인덱스 방식exchangeObjectAtIndex:withObjectAtIndex:가 이미 존재합니다.
아직 코멘트를 할 수 없기 때문에, 충분한 대응을 하고 싶다고 생각하고 있습니다.저는 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
Import의 경우GameplayKit
, 이 있습니다.shuffled
API:
https://developer.apple.com/reference/foundation/nsarray/1640855-shuffled
let shuffledArray = array.shuffled()
약간 개선되고 간결한 솔루션(상위 답변에 비해).
알고리즘은 동일하며 문헌에서는 "Fisher-Yates shuffle"로 설명되어 있습니다.
목표-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
Swift 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])
}
}
}
주의: Swift에서는 를 사용하여 iOS10에서 보다 간결한 솔루션을 사용할 수 있습니다.
주의: 불안정한 셔플링 알고리즘(카운트가 1을 넘으면 모든 위치가 강제로 변경됨)도 사용할 수 있습니다.
이것은 NSAray 또는 NSMutableArray를 섞는 가장 간단하고 빠른 방법입니다(개체 퍼즐은 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를 비교하여 반환할 수 있는 값은 다음과 같습니다.
- NSOrderdedAscending = - 1
- NSOrenderedSame = 0
- NSOrderdowning = 1
iOS 10부터는 GameplayKit의 NSAray를 사용할 수 있습니다.다음은 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())
}
}
GitHub에는 SSToolKit이라고 하는 이 메서드가 포함되어 있는 좋은 인기 라이브러리가 있습니다.파일 NSMutable Array+SSToolkitAdditions.h에는 shuffle 메서드가 포함되어 있습니다.사용하실 수도 있습니다.이 중에는 유용한 것들이 많이 있는 것 같다.
이 도서관의 메인 페이지는 여기에 있습니다.
이것을 사용하면, 코드는 다음과 같습니다.
#import <SSCategories.h>
NSMutableArray *tableData = [NSMutableArray arrayWithArray:[temp shuffledArray]];
이 라이브러리에는 Pod도 있습니다(CocoPods 참조).
요소에 반복이 있는 경우.
예: 어레이:A A B B 또는 B B A A
유일한 해결책은 A B A B A 입니다.
sequenceSelected
NSMutable Array, obj obj.
- (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의 대답은 꽤 좋지만 완전히 무작위적인 것은 아니다.
2개의 요소로 구성된 배열을 지정하면 나머지 인덱스에서 랜덤 범위를 생성하기 때문에 이 함수는 항상 반전된 배열을 반환합니다. 더 정확하게 말하면shuffle()
은 '하다, 하다, 하다'와 .
- (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];
}
}
}
편집: 올바르지 않습니다.참고로 저는 이 글을 삭제하지 않았습니다.이 방법이 올바르지 않은 이유에 대한 설명을 참조하십시오.
간단한 코드:
- (NSArray *)shuffledArray:(NSArray *)array
{
return [array sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
if (arc4random() % 2) {
return NSOrderedAscending;
} else {
return NSOrderedDescending;
}
}];
}
언급URL : https://stackoverflow.com/questions/56648/whats-the-best-way-to-shuffle-an-nsmutablearray
'source' 카테고리의 다른 글
ARC에서 항상 자신의 약한 참조를 블록에 전달하시겠습니까? (0) | 2023.04.09 |
---|---|
IF 문: 조건이 false일 경우 셀을 공백으로 두는 방법("does not function") (0) | 2023.04.09 |
Swift Language 추상 함수 (0) | 2023.04.09 |
iOS 앱이 백그라운드에 있는지 확인할 수 있는 방법이 있나요? (0) | 2023.04.09 |
왜 환경인가?Exit()는 더 이상 프로그램을 종료하지 않습니까? (0) | 2023.04.09 |