Swift で配列をシャッフルするにはどうすればいいですか? 質問する

Swift で配列をシャッフルするにはどうすればいいですか? 質問する

.shuffle() と .shuffled() はSwiftの一部です


元々の歴史的な質問:

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

ベストアンサー1

この回答では、Swift 4.2 以降で高速かつ均一なアルゴリズム (Fisher-Yates) を使用してシャッフルする方法と、以前のさまざまなバージョンの Swift に同じ機能を追加する方法について詳しく説明します。各 Swift バージョンの命名と動作は、そのバージョンの mutating ソート方法と nonmutating ソート方法と一致します。

Swift 4.2以降

shuffleそしてshuffledSwift 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]

スウィフト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]

おすすめ記事