.shuffle()和.shuffled()是Swift的一部分
原历史问题:
我如何随机或洗牌在Swift数组中的元素?例如,如果我的数组包含52张扑克牌,我想要洗牌数组以洗牌牌组。
.shuffle()和.shuffled()是Swift的一部分
原历史问题:
我如何随机或洗牌在Swift数组中的元素?例如,如果我的数组包含52张扑克牌,我想要洗牌数组以洗牌牌组。
当前回答
swift 4.2有两个方便的功能:
// shuffles the array in place
myArray.shuffle()
and
// generates a new array with shuffled elements of the old array
let newArray = myArray.shuffled()
其他回答
以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作为索引
在我的例子中,我在数组中交换对象时遇到了一些问题。然后我挠头,开始重新发明轮子。
// 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
}
}
当我将xCode版本升级到7.4 beta时,它停止在“swap(&self[i], &self[j])”。 致命错误:不支持与自身交换位置
我找到了I = j的原因(交换函数将爆炸)
所以我添加了一个条件,如下所示
if (i != j){
swap(&list[i], &list[j])
}
丫!对我来说没问题。
编辑:正如在其他回答中提到的,Swift 4.2最终在标准库中添加了随机数生成,并完成了数组变换。
然而,GameplayKit中的GKRandom / GKRandomDistribution套件仍然可以与新的RandomNumberGenerator协议一起使用-如果您添加扩展到GameplayKit rng以符合新的标准库协议,您可以轻松获得:
可发送的rng(在测试需要时可以复制“随机”序列) 为了速度而牺牲健壮性的rng 产生非均匀分布的rng
...并且仍然可以使用Swift中漂亮的新“本地”随机api。
这个答案的其余部分涉及到这些rng和/或它们在旧的Swift编译器中的使用。
这里已经有一些很好的答案,以及一些很好的说明为什么如果不小心,编写自己的shuffle可能容易出错。
在iOS 9、macOS 10.11和tvOS 9(或更高版本)中,你不必自己编写。GameplayKit中有一个有效且正确的Fisher-Yates执行方法(游戏邦注:尽管它的名字叫Fisher-Yates,但它并不只是用于游戏)。
如果你只是想要一个独特的洗牌:
let shuffled = GKRandomSource.sharedRandom().arrayByShufflingObjects(in: array)
如果您希望能够复制洗牌或一系列洗牌,请选择并播种一个特定的随机源;如。
let lcg = GKLinearCongruentialRandomSource(seed: mySeedValue)
let shuffled = lcg.arrayByShufflingObjects(in: array)
在iOS 10 / macOS 10.12 / tvOS 10中,也有一个方便的语法,可以通过NSArray的扩展进行变换。当然,当你使用Swift数组时,这有点麻烦(并且它在返回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()
这是我所使用的:
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]