.shuffle()和.shuffled()是Swift的一部分
原历史问题:
我如何随机或洗牌在Swift数组中的元素?例如,如果我的数组包含52张扑克牌,我想要洗牌数组以洗牌牌组。
.shuffle()和.shuffled()是Swift的一部分
原历史问题:
我如何随机或洗牌在Swift数组中的元素?例如,如果我的数组包含52张扑克牌,我想要洗牌数组以洗牌牌组。
当前回答
在Swift 3中,如果你想洗牌一个数组,或者从一个数组中获得一个新的洗牌数组,AnyIterator可以帮助你。其思想是从数组中创建一个索引数组,用AnyIterator实例和swap(_:_:)函数洗牌这些索引,并将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扩展中创建一个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扩展中创建一个shuffle()函数,以便在适当的位置洗牌数组:
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"]
其他回答
SWIFT 4
func createShuffledSequenceOfNumbers(max:UInt)->[UInt] {
var array:[UInt]! = []
var myArray:[UInt]! = []
for i in 1...max {
myArray.append(i)
}
for i in 1...max {
array.append(i)
}
var tempArray:[Int]! = []
for index in 0...(myArray.count - 1) {
var isNotFinded:Bool = true
while(isNotFinded){
let randomNumber = arc4random_uniform(UInt32(myArray.count))
let randomIndex = Int(randomNumber)
if(!tempArray.contains(randomIndex)){
tempArray.append(randomIndex)
array[randomIndex] = myArray[index]
isNotFinded = false
}
}
}
return array
}
当我将xCode版本升级到7.4 beta时,它停止在“swap(&self[i], &self[j])”。 致命错误:不支持与自身交换位置
我找到了I = j的原因(交换函数将爆炸)
所以我添加了一个条件,如下所示
if (i != j){
swap(&list[i], &list[j])
}
丫!对我来说没问题。
在Swift 4.2中,现在有一个可变shuffle和不可变shuffle的方法。你可以在这里阅读更多关于随机生成和数组的东西。
编辑:正如在其他回答中提到的,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()
工作阵列扩展(突变和非突变)
Swift 4.1 / Xcode 9
上面的答案已弃用,所以我自己创建了自己的扩展,在Swift的最新版本Swift 4.1 (Xcode 9)中洗牌数组:
extension Array {
// Non-mutating shuffle
var shuffled : Array {
let totalCount : Int = self.count
var shuffledArray : Array = []
var count : Int = totalCount
var tempArray : Array = self
for _ in 0..<totalCount {
let randomIndex : Int = Int(arc4random_uniform(UInt32(count)))
let randomElement : Element = tempArray.remove(at: randomIndex)
shuffledArray.append(randomElement)
count -= 1
}
return shuffledArray
}
// Mutating shuffle
mutating func shuffle() {
let totalCount : Int = self.count
var shuffledArray : Array = []
var count : Int = totalCount
var tempArray : Array = self
for _ in 0..<totalCount {
let randomIndex : Int = Int(arc4random_uniform(UInt32(count)))
let randomElement : Element = tempArray.remove(at: randomIndex)
shuffledArray.append(randomElement)
count -= 1
}
self = shuffledArray
}
}
调用非突变Shuffle [Array] -> [Array]:
let array = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
print(array.shuffled)
这将以随机顺序打印数组。
调用Shuffle [Array] = [Array]:
var array = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
array.shuffle()
// The array has now been mutated and contains all of its initial
// values, but in a randomized shuffled order
print(array)
这将打印数组的当前顺序,该顺序已经被随机打乱。
希望这对每个人都有用,如果你有任何问题,建议或评论,请随时提问!