如何在Swift中对数组进行洗牌?

如何在Swift中随机化或洗牌数组中的元素? 例如,如果我的数组由52张扑克牌组成,我想洗牌数组以洗牌。

这个答案详细说明了如何在Swift的各种版本中添加一个Fisher-Yates(快速统一)洗牌。 Swift 3和4版本是最宽松的,但它们至less都适用于数组。 每个Swift版本的命名和行为都与该版本的变异和非变异sorting方法相匹配。

斯威夫特4

这些扩展将shuffle()方法添加到任何可变集合(数组和不安全的可变缓冲区)和任何序列的shuffled()方法:

 extension MutableCollection { /// Shuffles the contents of this collection. mutating func shuffle() { let c = count guard c > 1 else { return } for (firstUnshuffled, unshuffledCount) in zip(indices, stride(from: c, to: 1, by: -1)) { let d: IndexDistance = numericCast(arc4random_uniform(numericCast(unshuffledCount))) let i = index(firstUnshuffled, offsetBy: d) swapAt(firstUnshuffled, i) } } } extension Sequence { /// Returns an array with the contents of this sequence, shuffled. func shuffled() -> [Element] { var result = Array(self) result.shuffle() return result } } let x = [1, 2, 3].shuffled() // x == [2, 3, 1] let fiveStrings = stride(from: 0, through: 100, by: 5).map(String.init).shuffled() // fiveStrings == ["20", "45", "70", "30", ...] var numbers = [1, 2, 3, 4] numbers.shuffle() // numbers == [3, 2, 1, 4] 

Swift 3

这些扩展将shuffle()方法添加到任何可变集合,并将shuffle()方法添加到任何序列中:

 extension MutableCollection where Indices.Iterator.Element == Index { /// Shuffles the contents of this collection. mutating func shuffle() { let c = count guard c > 1 else { return } for (firstUnshuffled , unshuffledCount) in zip(indices, stride(from: c, to: 1, by: -1)) { let d: IndexDistance = numericCast(arc4random_uniform(numericCast(unshuffledCount))) guard d != 0 else { continue } let i = index(firstUnshuffled, offsetBy: d) swap(&self[firstUnshuffled], &self[i]) } } } extension Sequence { /// Returns an array with the contents of this sequence, shuffled. func shuffled() -> [Iterator.Element] { var result = Array(self) result.shuffle() return result } } let x = [1, 2, 3].shuffled() // x == [2, 3, 1] let fiveStrings = stride(from: 0, through: 100, by: 5).map(String.init).shuffled() // fiveStrings == ["20", "45", "70", "30", ...] var numbers = [1, 2, 3, 4] numbers.shuffle() // numbers == [3, 2, 1, 4] 

Swift 2

 extension MutableCollectionType where Index == Int { /// Shuffle the elements of `self` in-place. mutating func shuffleInPlace() { // empty and single-element collections don't shuffle if count < 2 { return } for i in startIndex ..< endIndex - 1 { let j = Int(arc4random_uniform(UInt32(count - i))) + i guard i != j else { continue } swap(&self[i], &self[j]) } } } extension CollectionType { /// Return a copy of `self` with its elements shuffled. func shuffle() -> [Generator.Element] { var list = Array(self) list.shuffleInPlace() return list } } [1, 2, 3].shuffle() // [2, 3, 1] let fiveStrings = 0.stride(through: 100, by: 5).map(String.init).shuffle() // ["20", "45", "70", "30", ...] var numbers = [1, 2, 3, 4] numbers.shuffleInPlace() // [3, 2, 1, 4] 

Swift 1.2

shuffle作为一项function

这是最简单的版本 :在顶层的任何地方添加这个函数,你就可以洗牌数组和片段:

 func shuffle<C: MutableCollectionType where C.Index == Int>(var list: C) -> C { let c = count(list) if c < 2 { return list } for i in 0..<(c - 1) { let j = Int(arc4random_uniform(UInt32(c - i))) + i swap(&list[i], &list[j]) } return list } shuffle([1, 2, 3, 4, 5, 6, 7, 8]) // eg, [6, 1, 8, 3, 2, 4, 7, 5] shuffle(["hello", "goodbye", "ciao"]) // eg, ["ciao", "goodbye", "hello"] 

作为变异数组方法进行shuffle

这个扩展可以让你在适当的地方洗一个可变的Array实例:

 extension Array { mutating func shuffle() { if count < 2 { return } for i in 0..<(count - 1) { let j = Int(arc4random_uniform(UInt32(count - i))) + i swap(&self[i], &self[j]) } } } var numbers = [1, 2, 3, 4, 5, 6, 7, 8] numbers.shuffle() // eg, numbers == [6, 1, 8, 3, 2, 4, 7, 5] 

作为非变异数组方法进行shuffled

这个扩展可以让你检索一个Array实例的混洗副本:

 extension Array { func shuffled() -> [T] { if count < 2 { return self } var list = self for i in 0..<(list.count - 1) { let j = Int(arc4random_uniform(UInt32(list.count - i))) + i swap(&list[i], &list[j]) } return list } } let numbers = [1, 2, 3, 4, 5, 6, 7, 8] let mixedup = numbers.shuffled() // eg, mixedup == [6, 1, 8, 3, 2, 4, 7, 5] 

这里有一些很好的答案,以及一些很好的例子,说明如果你不小心,编写自己的洗牌程序可能会出错。

在iOS 9,MacOS 10.11和tvOS 9(或更高版本)中,您不必编写自己的。 在GameplayKit中有一个Fisher-Yates的高效,正确的实现 (尽pipe名字不仅仅是游戏)。

如果你只是想要一个独特的洗牌:

 let shuffled = GKRandomSource.sharedRandom().arrayByShufflingObjects(in: array) 

如果您希望能够复制随机播放或一系列随机播放,请select并播种特定的随机播放源; 例如

 let lcg = GKLinearCongruentialRandomSource(seed: mySeedValue) let shuffled = lcg.arrayByShufflingObjects(in: array) 

在iOS 10 / macOS 10.12 / tvOS 10中,还有一个方便的语法,用于通过NSArray上的扩展进行混洗。 当然,使用Swift Array有点麻烦(并且在返回Swift时会丢失它的元素types):

 let shuffled1 = (array as NSArray).shuffled(using: random) // -> [Any] let shuffled2 = (array as NSArray).shuffled() // use default random source 

但是为它制作一个保存types的Swift包装是相当容易的:

 extension Array { func shuffled(using source: GKRandomSource) -> [Element] { return (self as NSArray).shuffled(using: source) as! [Element] } func shuffled() -> [Element] { return (self as NSArray).shuffled() as! [Element] } } let shuffled3 = array.shuffled(using: random) let shuffled4 = array.shuffled() 

这里可能有些短一些:

 sorted(a) {_, _ in arc4random() % 2 == 0} 

Swift 2.0中 ,GameplayKit可能会来拯救! (由iOS9或更高版本支持)

 import GameplayKit func shuffle() { array = GKRandomSource.sharedRandom().arrayByShufflingObjectsInArray(array) } 

以Nate的algorithm,我想看看这将如何与Swift 2和协议扩展。

这是我想出来的。

 extension MutableCollectionType where Self.Index == Int { mutating func shuffleInPlace() { let c = self.count for i in 0..<(c - 1) { let j = Int(arc4random_uniform(UInt32(c - i))) + i swap(&self[i], &self[j]) } } } extension MutableCollectionType where Self.Index == Int { func shuffle() -> Self { var r = self let c = self.count for i in 0..<(c - 1) { let j = Int(arc4random_uniform(UInt32(c - i))) + i swap(&r[i], &r[j]) } return r } } 

现在,任何MutableCollectionType都可以使用这些方法,因为它使用Int作为Index

在我的情况下,我有一些交换数组中的对象的问题。 然后,我挠了挠脑袋,开始重新发明轮子。

 // swift 3.0 ready extension Array { func shuffled() -> [Element] { var results = [Element]() var indexes = (0 ..< count).map { $0 } while indexes.count > 0 { let indexOfIndexes = Int(arc4random_uniform(UInt32(indexes.count))) let index = indexes[indexOfIndexes] results.append(self[index]) indexes.remove(at: indexOfIndexes) } return results } } 

这是我使用的:

 func newShuffledArray(array:NSArray) -> NSArray { var mutableArray = array.mutableCopy() as! NSMutableArray var count = mutableArray.count if count>1 { for var i=count-1;i>0;--i{ mutableArray.exchangeObjectAtIndex(i, withObjectAtIndex: Int(arc4random_uniform(UInt32(i+1)))) } } return mutableArray as NSArray } 

Swift 3解决scheme,遵循@Nate Cook的答案:(如果索引从0开始,请参阅下面的注释)

 extension Collection { /// Return a copy of `self` with its elements shuffled func shuffle() -> [Generator.Element] { var list = Array(self) list.shuffleInPlace() return list } } extension MutableCollection where Index == Int { /// Shuffle the elements of `self` in-place. mutating func shuffleInPlace() { // empty and single-element collections don't shuffle if count < 2 { return } let countInt = count as! Int for i in 0..<countInt - 1 { let j = Int(arc4random_uniform(UInt32(countInt - i))) + i guard i != j else { continue } swap(&self[i], &self[j]) } } } 

这是如何以最简单的方式完成的。 import Gamplaykit你的VC并使用下面的代码。 testing在Xcode 8。

  import GameplayKit let array: NSArray = ["Jock", "Ellie", "Sue Ellen", "Bobby", "JR", "Pamela"] override func viewDidLoad() { super.viewDidLoad() print(array.shuffled()) } 

如果你想从数组中获得一个混洗的string,你可以使用下面的代码..

 func suffleString() { let ShuffleArray = array.shuffled() suffleString.text = ShuffleArray.first as? String print(suffleString.text!) } 

使用Swift 3,如果你想要在一个数组中混洗一个数组, AnyIterator从一个数组中获得一个新的混洗数组, AnyIterator可以帮助你。 这个想法是从你的数组中创build一个索引数组,以便用AnyIterator实例和swap(_:_:)函数对这些索引进行混洗, AnyIterator这个AnyIterator实例的每个元素与数组的相应元素进行映射。


以下Playground代码显示了它的工作原理:

 import Darwin // required for arc4random_uniform let array = ["Jock", "Ellie", "Sue Ellen", "Bobby", "JR", "Pamela"] var indexArray = Array(array.indices) var index = indexArray.endIndex let indexIterator: AnyIterator<Int> = AnyIterator { guard let nextIndex = indexArray.index(index, offsetBy: -1, limitedBy: indexArray.startIndex) else { return nil } index = nextIndex let randomIndex = Int(arc4random_uniform(UInt32(index))) if randomIndex != index { swap(&indexArray[randomIndex], &indexArray[index]) } return indexArray[index] } let newArray = indexIterator.map { array[$0] } print(newArray) // may print: ["Jock", "Ellie", "Sue Ellen", "JR", "Pamela", "Bobby"] 

您可以重构以前的代码,并在Array扩展中创build一个shuffled()函数,以便从数组中获得新的混洗数组:

 import Darwin // required for arc4random_uniform extension Array { func shuffled() -> Array<Element> { var indexArray = Array<Int>(indices) var index = indexArray.endIndex let indexIterator = AnyIterator<Int> { guard let nextIndex = indexArray.index(index, offsetBy: -1, limitedBy: indexArray.startIndex) else { return nil } index = nextIndex let randomIndex = Int(arc4random_uniform(UInt32(index))) if randomIndex != index { swap(&indexArray[randomIndex], &indexArray[index]) } return indexArray[index] } return indexIterator.map { self[$0] } } } 

用法:

 let array = ["Jock", "Ellie", "Sue Ellen", "Bobby", "JR", "Pamela"] let newArray = array.shuffled() print(newArray) // may print: ["Bobby", "Pamela", "Jock", "Ellie", "JR", "Sue Ellen"] 
 let emptyArray = [String]() let newEmptyArray = emptyArray.shuffled() print(newEmptyArray) // prints: [] 

作为前面代码的替代方法,您可以在Array扩展中创build一个shuffle()函数,以便将数组随机sorting:

 import Darwin // required for arc4random_uniform extension Array { mutating func shuffle() { var indexArray = Array<Int>(indices) var index = indexArray.endIndex let indexIterator = AnyIterator<Int> { guard let nextIndex = indexArray.index(index, offsetBy: -1, limitedBy: indexArray.startIndex) else { return nil } index = nextIndex let randomIndex = Int(arc4random_uniform(UInt32(index))) if randomIndex != index { swap(&indexArray[randomIndex], &indexArray[index]) } return indexArray[index] } self = indexIterator.map { self[$0] } } } 

用法:

 var mutatingArray = ["Jock", "Ellie", "Sue Ellen", "Bobby", "JR", "Pamela"] mutatingArray.shuffle() print(mutatingArray) // may print ["Sue Ellen", "Pamela", "Jock", "Ellie", "Bobby", "JR"] 

这是Nate实现 Swift 4 的Fisher-Yates shuffle的一个版本(Xcode 9,目前处于testing阶段)。

 extension MutableCollection { /// Shuffle the elements of `self` in-place. mutating func shuffle() { // Empty and single-element collections don't shuffle if count < 2 { return } for i in indices.dropLast() { let diff = distance(from: i, to: endIndex) let j = index(i, offsetBy: numericCast(arc4random_uniform(numericCast(diff)))) swapAt(i, j) } } } extension Collection { /// Return a copy of `self` with its elements shuffled func shuffled() -> [Element] { var list = Array(self) list.shuffle() return list } } 

这些变化是:

  • 约束Indices.Iterator.Element == Index现在是Collection协议的一部分,不再需要强加在扩展上。
  • 交换元素必须通过调用集合上的swapAt()来完成,比较SE-0173添加MutableCollection.swapAt(_:_:)
  • ElementIterator.Element的别名。

这是如何在Swift 3.0中用一个种子来混洗一个数组。

 extension MutableCollection where Indices.Iterator.Element == Index { mutating func shuffle() { let c = count guard c > 1 else { return } for (firstUnshuffled , unshuffledCount) in zip(indices, stride(from: c, to: 1, by: -1)) { srand48(seedNumber) let number:Int = numericCast(unshuffledCount) let r = floor(drand48() * Double(number)) let d: IndexDistance = numericCast(Int(r)) guard d != 0 else { continue } let i = index(firstUnshuffled, offsetBy: d) swap(&self[firstUnshuffled], &self[i]) } } } 
 let shuffl = GKRandomSource.sharedRandom().arrayByShufflingObjects(in: arrayObject) 

这是我使用的:

 import GameplayKit extension Collection { func shuffled() -> [Iterator.Element] { let shuffledArray = (self as? NSArray)?.shuffled() let outputArray = shuffledArray as? [Iterator.Element] return outputArray ?? [] } mutating func shuffle() { if let selfShuffled = self.shuffled() as? Self { self = selfShuffled } } } // Usage example: var numbers = [1,2,3,4,5] numbers.shuffle() print(numbers) // output example: [2, 3, 5, 4, 1] print([10, "hi", 9.0].shuffled()) // output example: [hi, 10, 9] 

简单的例子:

 extension Array { mutating func shuffled() { for _ in self { // generate random indexes that will be swapped var (a, b) = (Int(arc4random_uniform(UInt32(self.count - 1))), Int(arc4random_uniform(UInt32(self.count - 1)))) if a == b { // if the same indexes are generated swap the first and last a = 0 b = self.count - 1 } swap(&self[a], &self[b]) } } } var array = [1,2,3,4,5,6,7,8,9,10] array.shuffled() print(array) // [9, 8, 3, 5, 7, 6, 4, 2, 1, 10] 

在Wikipedia上形成Fisher-Yates洗牌的文章

Swift版本3.1

一个)。 铅笔纸方法:

 func shuffle<T>(_ array:inout [T]){ var temp = [T]() for _ in array{ /*Generating random number with length*/ let random = arc4random_uniform(UInt32(array.count)) /*Take the element from array*/ let elementTaken = array[Int(random)] /*Append it to new tempArray*/ temp.append(elementTaken) /*Remove the element from array*/ array.remove(at: Int(random)) } /* array = tempArray*/ array = temp } 

B)。 现代方法:( Durstenfeld的版本)

 func shuffle<T>(_ array:inout [T]){ var length = array.count for _ in array{ /*Generating random number with length*/ let random = arc4random_uniform(UInt32(length)) /*Check before index of two elements not same*/ if length-1 != Int(random){ swap(&array[length-1], &array[Int(random)]) } length -= 1 } } 

Extension:

一个)。 铅笔纸方法:

 extension Array{ mutating func shuffled(){ var temp = [Element]() for _ in self{ /*Generating random number with length*/ let random = arc4random_uniform(UInt32(self.count)) /*Take the element from array*/ let elementTaken = self[Int(random)] /*Append it to new tempArray*/ temp.append(elementTaken) /*Remove the element from array*/ self.remove(at: Int(random)) } /* array = tempArray*/ self = temp } } 

B)。 现代方法:( Durstenfeld的版本)

 extension Array{ mutating func shuffled(){ var length = self.count for _ in self{ /*Generating random number with length*/ let random = arc4random_uniform(UInt32(length)) /*Check before index of two elements not same*/ if length-1 != Int(random){ /*Swaping elements, If same index then there is no swap*/ // swap(&self[length-1], &self[Int(random)]) -> Swift 3.0 self.swapAt(length-1, Int(random)) //-> Swift 4.0 } length -= 1 } } } 

参考:

 /* By using shuffle functions*/ var a = [1,2,3,4,5,6,7,8,9,10] for _ in 1...10{ self.shuffle(&a) /*For shuffled extension, a.shuffled()*/ print(a) } 

注意:你也可以使用空数组。

输出:

[6,2,10,5,1,8,9,4,3,7]

[7,1,9,8,2,10,5,6,4,3]

[8,9,6,10,5,2,7,4,3,1]

[10,1,7,4,8,9,3,5,2,6]

[8,1,6,9,3,7,4,5,10,2]

[4,3,7,9,1,5,8,6,10,2]

[7,3,4,9,10,1,6,5,2,8]

[3,6,2,4,5,8,9,7,1,10]

[5,1,2,10,6,9,7,3,8,4]

[7,9,3,8,2,1,5,4,6,10]

请让我知道,如果有任何疑问,其他Swift版本将尽快检查。

您也可以使用通用swapfunction,并实现提到的Fisher-Yates:

 for idx in 0..<arr.count { let rnd = Int(arc4random_uniform(UInt32(idx))) if rnd != idx { swap(&arr[idx], &arr[rnd]) } } 

或更less冗长:

 for idx in 0..<steps.count { swap(&steps[idx], &steps[Int(arc4random_uniform(UInt32(idx)))]) } 

这是一些在操场上运行的代码。 您不需要在实际的Xcode项目中导入Darwin。

 import darwin var a = [1,2,3,4,5,6,7] func shuffle<ItemType>(item1: ItemType, item2: ItemType) -> Bool { return drand48() > 0.5 } sort(a, shuffle) println(a) 

当我将xCode版本升级到7.4testing版时,它停在“swap(&self [i],&self [j])”。
致命错误:不支持自动交换位置

我find了i = j的原因(swap的function会爆炸)

所以我添加一个条件如下

 if (i != j){ swap(&list[i], &list[j]) } 

YA! 对我来说还好。