什么是最好的方式来洗牌NSMutableArray?
-
09-06-2019 - |
题
如果你有一个 NSMutableArray
, 你怎么随机的随机因素?
(我有我自己回答这个,这是贴在下面,但我是新来的可可和我有兴趣知道,如果有一个更好的方式。)
更新:注意到@穆克什,作为iOS10+和mac os10.12+,还有一个 -[NSMutableArray shuffledArray]
方法,可以用来洗牌。看看 https://developer.apple.com/documentation/foundation/nsarray/1640855-shuffledarray?language=objc 对于细节。(但注意,这创造了一个新阵列,而不是洗的元素的地方。)
解决方案
你不需要swapObjectAtIndex方法。 exchangeObjectAtIndex:withObjectAtIndex: 已经存在。
其他提示
我解决了这个通过增加一个类别NSMutableArray.
编辑: 去除不必要的方法感到答案由拉德.
编辑: 改变了 (arc4random() % nElements)
要 arc4random_uniform(nElements)
谢谢你回答的格雷戈里Goltsov和评论意见miho和blahdiblah
编辑: 循环的改善,感谢意见罗恩
编辑: 加入检查,一阵是不是空的,谢谢来见马赫什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
因为我可能没有意见,我想我会做出贡献的一个全面响应。我修改教授约翰逊的执行对我的项目在一定数量的方式(真的想让它尽可能简洁),其中之一 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
从iOS10您可以使用新的 shuffled
API:
https://developer.apple.com/reference/foundation/nsarray/1640855-shuffled
let shuffledArray = array.shuffled()
一个稍有改进和简明的解决方案(相比的顶答)。
算法是相同的,是文献中所述的"Fisher-耶茨洗牌".
在目标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))))
}
}
}
在迅速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])
}
}
}
这是最简单和最快的方式来洗牌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
有一个很好的受欢迎的图书馆,这种方法,因为它的一部分,称为 SSToolKit在。.文件NSMutableArray+SSToolkitAdditions.h包含洗牌的方法。你可以用它。在此之中,似乎有很多实用的东西。
主页这是图书馆 在这里,.
如果你用这个,你的代码会是这样的:
#import <SSCategories.h>
NSMutableArray *tableData = [NSMutableArray arrayWithArray:[temp shuffledArray]];
该图书馆还有一个Pod(见CocoaPods)
从iOS10,可以使用 仅 shuffled()
从GameplayKit.这是一个帮助列在迅速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())
}
}
如果元素有重复。
例如阵列:一个B B或B一个
唯一的解决办法是:B一个B
sequenceSelected
是NSMutableArray其中存储的元素类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];
教授约翰逊的答案 是相当不错,但它不是完全随机的。
鉴于一系列的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;
}
}];
}