1. ホーム

[解決済み】Swiftで配列をシャッフルするにはどうすればいいですか?

2022-03-26 02:42:53

質問

Swift で配列内の要素をランダム化またはシャッフルするにはどうすればよいですか?たとえば、私の配列が52枚のトランプで構成されている場合、私は次のことをしたいです。 シャッフル を使用すると、デッキをシャッフルすることができます。

どのように解決するのですか?

この回答は、Swift 4.2+ で高速で均一なアルゴリズム (Fisher-Yates) でシャッフルする方法と、Swift の様々な以前のバージョンで同じ機能を追加する方法を詳細に説明します。各Swiftバージョンの命名と動作は、そのバージョンの変異と非変異のソートメソッドに一致します。

Swift 4.2+

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]

Swift 4.0と4.1

これらの拡張機能では 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の例と同じ使い方です。


スウィフト3

これらのエクステンションは 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の例と同じ使い方です。


スウィフト2

(廃止言語: 2018年7月からiTunes Connectでの公開にSwift 2.xを使用することはできません)

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]


Swift 1.2

(廃止言語: 2018年7月からiTunes Connectでの公開にSwift 1.xを使用することはできません)

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]