如何在Swift中随机化或混洗数组中的元素?例如,如果我的阵列由52张扑克牌,我想洗牌的阵列,以洗牌.
在Swift 2.0中,GameplayKit可能会来救援!(iOS9或更高版本支持)
import GameplayKit func shuffle() { array = GKRandomSource.sharedRandom().arrayByShufflingObjectsInArray(array) }
采用Nate的算法,我想看看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
在我的例子中,我遇到了在Array中交换对象的一些问题.然后我挠挠头去重新发明轮子.
// 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 } }
这是Nate为Swift 4 (Xcode 9)实现Fisher-Yates shuffle的一个版本.
extension MutableCollection { /// Shuffle the elements of `self` in-place. mutating func shuffle() { 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 AddMutableCollection.swapAt(_:_:)
.
Element
是别名Iterator.Element
.
编辑:正如其他答案所述,Swift 4.2 最终将随机数生成添加到标准库中,完成数组重排.
但是,GameplayKit中的GKRandom
/ GKRandomDistribution
suite仍然可以用于新RandomNumberGenerator
协议 - 如果您向GameplayKit RNG添加扩展以符合新的标准库协议,您可以轻松获得:
可发送的RNG(可在需要进行测试时重现"随机"序列)
RNG牺牲了速度的稳健性
产生非均匀分布的RNG
...并且仍然使用Swift中漂亮的新"本机"随机API.
本答案的其余部分涉及此类RNG和/或它们在较旧的Swift编译器中的使用.
这里有一些很好的答案,以及一些很好的例子,说明如果你不小心,写你自己的shuffle可能容易出错.
在iOS 9,macOS 10.11和tvOS 9(或更高版本)中,您不必编写自己的.在GameplayKit中有一个高效,正确的Fisher-Yates实现(尽管名称不仅仅适用于游戏).
如果你只想要一个独特的shuffle:
let shuffled = GKRandomSource.sharedRandom().arrayByShufflingObjects(in: array)
如果您希望能够复制shuffle或一系列shuffle,请选择并播种特定的随机源; 例如
let lcg = GKLinearCongruentialRandomSource(seed: mySeedValue) let shuffled = lcg.arrayByShufflingObjects(in: array)
在iOS 10/macOS 10.12/tvOS 10中,还有一个方便的语法,可以通过扩展来进行混洗NSArray
.当然,当你使用Swift时Array
它会有点麻烦(它在返回Swift时会失去它的元素类型):
let shuffled1 = (array as NSArray).shuffled(using: random) // -> [Any] let shuffled2 = (array as NSArray).shuffled() // use default random source
但是为它创建一个类型保留的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 4.2+中使用快速统一的算法(Fisher-Yates),以及如何在各种先前版本的Swift中添加相同的功能.每个Swift版本的命名和行为都与该版本的变异和非变异排序方法相匹配.
shuffle
并且shuffled
是原生的Swift 4.2.用法示例:
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]
这些扩展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)) { // Change `Int` in the next line to `IndexDistance` in < Swift 4.1 let d: Int = 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 } }
与上面的Swift 4.2示例中的用法相同.
这些扩展shuffle()
为任何可变集合添加方法,并shuffled()
为任何序列添加方法:
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)) { // Change `Int` in the next line to `IndexDistance` in < Swift 3.2 let d: Int = numericCast(arc4random_uniform(numericCast(unshuffledCount))) guard d != 0 else { continue } let i = index(firstUnshuffled, offsetBy: d) self.swapAt(firstUnshuffled, 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 } }
与上面的Swift 4.2示例中的用法相同.
(过时的语言:从2018年7月开始,你无法使用Swift 2.x在iTunes Connect上发布)
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]
(过时的语言:从2018年7月开始,你不能使用Swift 1.x在iTunes Connect上发布)
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() // e.g., 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() // e.g., mixedup == [6, 1, 8, 3, 2, 4, 7, 5]