What's the Best Way to Shuffle an NSMutableArray?
如果你有一个
(我有自己的答案,这是张贴在下面,但我是新的可可,我有兴趣知道是否有更好的方法。)
更新:正如@mukesh所指出的,从iOS 10+和MacOS 10.12+开始,有一个
我通过向nsmutablearray添加一个类别来解决这个问题。
编辑:删除了不必要的方法,感谢LADD的回答。
编辑:由于Gregory Goltsov的回答和Miho和Blahdiblah的评论,
编辑:循环改进,感谢Ron的评论
编辑:添加检查数组是否不为空,这要感谢Mahesh Agrawal的评论。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | // 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 |
你不需要swapobjectatindex方法。ExchangeObjectAtindex:WithObjectAtindex:已存在。
既然我还不能发表评论,我想我会给出一个完整的回应。我以多种方式修改了Kristopher Johnson对我的项目的实现(真的试图使其尽可能简洁),其中之一是
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | // 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 |
从iOS 10可以使用新的
https://developer.apple.com/reference/foundation/nsarray/1640855-无序播放
1 | let shuffledArray = array.shuffled() |
号
一个稍微改进和简洁的解决方案(与最重要的答案相比)。
该算法与文献中描述的"Fisher-Yates shuffle"相同。
在目标C中:
1 2 3 4 5 6 7 8 | @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中:
1 2 3 4 5 6 7 8 | 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中:
1 2 3 4 5 6 7 8 9 | 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]) } } } |
号
注:使用
注:还提供了一种不稳定洗牌的算法(如果计数>1,则强制更改所有位置)。
这是最简单和最快的方法来洗牌NSarray或NSmutableArrays(对象拼图是一个非可变数组,它包含拼图对象。我已添加到指示数组中初始位置的益智对象变量索引)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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++; } } |
号
日志输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #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
- nsOrderedName=0
- nsOrderedDescending=1
从iOS 10,您可以使用游戏工具包中的nsarray
1 2 3 4 5 6 7 8 9 10 11 12 | 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。文件nsmutablerray+sstoolkitadditions.h包含shuffle方法。你也可以用它。其中,似乎有很多有用的东西。
这个图书馆的主页在这里。
如果使用此代码,则代码如下:
1 2 | #import <SSCategories.h> NSMutableArray *tableData = [NSMutableArray arrayWithArray:[temp shuffledArray]]; |
。
这个图书馆也有一个豆荚(见椰子)
如果元素有重复。
例如阵列:A A A B或B B A A
唯一的解决办法是:A B A B A
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | - (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; } } } } } |
。
克里斯托弗·约翰逊的回答很好,但并不是完全随机的。
给定一个由2个元素组成的数组,此函数总是返回逆数组,因为生成的是剩余索引的随机范围。更精确的
1 2 3 4 5 6 7 8 9 10 | - (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]; } } } |
1 | NSUInteger randomIndex = arc4random() % [theArray count]; |
。
编辑:这不正确。出于参考目的,我没有删除这篇文章。请参阅有关此方法不正确的原因的注释。
此处为简单代码:
1 2 3 4 5 6 7 8 9 10 | - (NSArray *)shuffledArray:(NSArray *)array { return [array sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) { if (arc4random() % 2) { return NSOrderedAscending; } else { return NSOrderedDescending; } }]; } |
。