.shuffle()和.shuffled()是Swift的一部分


原历史问题:

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


当前回答

在Swift 4.2中,现在有一个可变shuffle和不可变shuffle的方法。你可以在这里阅读更多关于随机生成和数组的东西。

其他回答

这是如何在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)

编辑:正如在其他回答中提到的,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()

这是最简单的方法。导入Gamplaykit到你的VC中,使用下面的代码。在Xcode 8中测试。

 import GameplayKit

 let array: NSArray = ["Jock", "Ellie", "Sue Ellen", "Bobby", "JR", "Pamela"]

 override func viewDidLoad() {
    super.viewDidLoad()

    print(array.shuffled())  
}

如果你想从数组中获得一个打乱的字符串,你可以使用下面的代码..

func suffleString() {

    let ShuffleArray = array.shuffled()

    suffleString.text = ShuffleArray.first as? String

    print(suffleString.text!)

}

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

我找到了I = j的原因(交换函数将爆炸)

所以我添加了一个条件,如下所示

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

丫!对我来说没问题。