.shuffle()和.shuffled()是Swift的一部分
原历史问题:
我如何随机或洗牌在Swift数组中的元素?例如,如果我的数组包含52张扑克牌,我想要洗牌数组以洗牌牌组。
.shuffle()和.shuffled()是Swift的一部分
原历史问题:
我如何随机或洗牌在Swift数组中的元素?例如,如果我的数组包含52张扑克牌,我想要洗牌数组以洗牌牌组。
当前回答
这是Nate为Swift 4实现的Fisher-Yates shuffle的一个版本 (Xcode 9)。
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
}
}
这些变化是:
约束indexes . iterator . element == Index现在是部分 的收集协议,并且不需要强加于 扩展了。 交换元素必须通过调用集合上的swapAt()来完成, 添加mutableccollection . swapat(_:_:)。 Element是Iterator.Element的别名。
其他回答
如果你想使用简单的Swift For循环函数,使用这个->
var arrayItems = ["A1", "B2", "C3", "D4", "E5", "F6", "G7", "H8", "X9", "Y10", "Z11"]
var shuffledArray = [String]()
for i in 0..<arrayItems.count
{
let randomObject = Int(arc4random_uniform(UInt32(items.count)))
shuffledArray.append(items[randomObject])
items.remove(at: randomObject)
}
print(shuffledArray)
Swift Array使用扩展名->
extension Array {
// Order Randomize
mutating func shuffle() {
for _ in 0..<count {
sort { (_,_) in arc4random() < arc4random() }
}
}
}
编辑:正如在其他回答中提到的,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 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
}
}
工作! !生物体是要洗牌的数组。
extension Array
{
/** Randomizes the order of an array's elements. */
mutating func shuffle()
{
for _ in 0..<10
{
sort { (_,_) in arc4random() < arc4random() }
}
}
}
var organisms = [
"ant", "bacteria", "cougar",
"dog", "elephant", "firefly",
"goat", "hedgehog", "iguana"]
print("Original: \(organisms)")
organisms.shuffle()
print("Shuffled: \(organisms)")
Swift 3解决方案,遵循@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])
}
}
}