LoginSignup
1
0

More than 5 years have passed since last update.

たかがシャッフル、されどシャッフル 〜"permute-with-all" order biasの検証〜

Last updated at Posted at 2018-04-26

先日、
【Swift4】 ArrayをShuffleする
というタイトルで投稿しました。

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

上記の方法では、結果に偏りが生じるというコメントを頂き、以下のサイトを教えて頂きました。
フィッシャー–イェーツのシャッフル:誤った実装による順序の偏り
それを踏まえて、"permute-with-all" order biasを検証してみました。

        //要素数 = n,試行回数 = repeatCount (自由に変更して下さい)
        let n = 5
        let repeatCount = 100000
        //twoDimArray[要素番号][試行回数何回目か] = 移動先の番号
        var twoDimArray = [[Int]]()
        let originalArray = Array<Int>(0 ..< n)
        for _ in 0 ..< n{
            twoDimArray.append([])
        }
        for _ in 0 ..< repeatCount{
            var anArray = originalArray
            for i in 0 ..< n{
                let r = Int(arc4random_uniform(UInt32(n)))
                anArray.swapAt(i, r)
                }
            for i in 0 ..< n{
                twoDimArray[i].append(anArray.index(of: i)!)
                }
        }
        for i in 0 ..< n{
            var postShuffleCite = [Int](repeating: 0, count: n)
            for j in 0 ..< repeatCount{
                postShuffleCite[twoDimArray[i][j]] += 1
            }
            for k in 0 ..< n{
                let probability = Double(postShuffleCite[k]) / Double(repeatCount) * 100.0
                print("\(i)\(k)番目に移動する確率は、\(probability)%")
            }
            print("\n")
        }

n = 5、試行回数10万回で検証して以下のような結果を得ました。

0が0番目に移動する確率は、19.839%
0が1番目に移動する確率は、20.138%
0が2番目に移動する確率は、20.078%
0が3番目に移動する確率は、19.942%
0が4番目に移動する確率は、20.003%

1が0番目に移動する確率は、24.255%
1が1番目に移動する確率は、18.19%
1が2番目に移動する確率は、18.485%
1が3番目に移動する確率は、19.104%
1が4番目に移動する確率は、19.966%

2が0番目に移動する確率は、20.902%
2が1番目に移動する確率は、22.843%
2が2番目に移動する確率は、17.436%
2が3番目に移動する確率は、18.719%
2が4番目に移動する確率は、20.1%

3が0番目に移動する確率は、18.423%
3が1番目に移動する確率は、20.351%
3が2番目に移動する確率は、23.144%
3が3番目に移動する確率は、18.018%
3が4番目に移動する確率は、20.064%

4が0番目に移動する確率は、16.581%
4が1番目に移動する確率は、18.478%
4が2番目に移動する確率は、20.857%
4が3番目に移動する確率は、24.217%
4が4番目に移動する確率は、19.867%

・・・かなり大きな偏りが出るようです。
ちなみにn = 7にすると「フィッシャー–イェーツのシャッフル (Wikipedia)」の"permute-with-all" order biasのサンプルとほぼ同様の結果が得られました。

※※偏りをなくす方法※※

let r = Int(arc4random_uniform(UInt32(n)))

を以下のように書き換えます。

let r = Int(arc4random_uniform(UInt32(n - 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 ..< n{
                let r = Int(arc4random_uniform(UInt32(n)))
                anArray.swapAt(i, r)
                }

の部分を

anArray.shuffle()

と書き換えます。
extensionの中身を色々書き換えて上記コードを用いれば、shuffle後の偏りの検証に使えますので皆様ご利用下さい。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0