【Swift4】 ArrayをShuffleする


Swift4で配列をシャッフルする簡単なサンプルを提示します。
"阿部","伊東","佐藤","鈴木","田中","中村","藤井","松井","村田","吉田"
と五十音順に並んだ人名の配列(anArray)をシャッフルします。

var anArray = ["阿部","伊東","佐藤","鈴木","田中","中村","藤井","松井","村田","吉田"]
        //shuffle前print
        print("shuffle前")
        for i in anArray{
            print("\(i)")
        }
        //shuffleする
        for i in 0 ..< anArray.count{
            let r = Int(arc4random_uniform(UInt32(anArray.count)))
            anArray.swapAt(i, r)
        }
        //shuffle後print
        print("shuffle後")
        for i in anArray{
            print("\(i)")
        }

print結果は以下のようになります。

shuffle前
阿部
伊東
佐藤
鈴木
田中
中村
藤井
松井
村田
吉田
shuffle後
佐藤
村田
田中
阿部
鈴木
藤井
伊東
中村
吉田
松井

※※追記※※
上記のコードでは結果に偏りが生じるという御指摘を、@t-ae 様より頂きました。そこで以下の新スレで検証してみました。
たかがシャッフル、されどシャッフル 〜"permute-with-all" order biasの検証〜
結論をいいますと、上記コードで生じる偏りは「かなり大きい」です。
従いまして、上記コードは使わない方がよいです

解決策

let r = Int(arc4random_uniform(UInt32(anArray.count)))

を以下のように変更します。

let r = Int(arc4random_uniform(UInt32(anArray.count - i))) + i

これにより、結果の偏りは無くなります。
以下のようにextensionにする方法もあります。

extension Array{
    mutating func shuffle(){
        let n = self.count
        for i in 0 ..< n{
            let r = Int(arc4random_uniform(UInt32(n - i))) + i
            self.swapAt(i, r)
        }
    }
}

別解

for i in 0 ..< anArray.count{
            let r = Int(arc4random_uniform(UInt32(anArray.count)))
            anArray.swapAt(i, r)
        }

のプロセスを以下のように2回繰り返すだけでも偏りがなくなります。

for _ in 0 ... 1{
        for i in 0 ..< anArray.count{
            let r = Int(arc4random_uniform(UInt32(anArray.count)))
            anArray.swapAt(i, r)
        }
}