くりにっき

フルスタックキュアエンジニアです

Ruby 1.9と2.0でArray#shuffleの結果を同じにしたかった

Rubyで使用されている乱数は Mersenne Twister というもので、同じseedを与えれば同じ乱数を生成してくれます

1.9.3p429 :001 > Random.new(445).rand
 => 0.6432614612049729

2.0.0-p195 :001 > Random.new(445).rand
 => 0.6432614612049729

この間1.9系と2.0系に対応したライブラリを書いていたのですが、rubyのメジャーバージョン間でArray#shuffleの結果が変わっていたので戸惑いました

1.9.3p429 :002 > [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].shuffle(random: Random.new(445))
 => [4, 2, 10, 8, 6, 5, 3, 9, 1, 7]

2.0.0-p195 :002 > [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].shuffle(random: Random.new(445))
 => [8, 10, 6, 7, 5, 1, 3, 2, 9, 4]

seedが固定されて同じ乱数が返ってきているハズなのに、なぜかshuffleした結果が違うという。。。

調べてみたらruby 2.0で Array#shuffle の実装が変わっているようでした

配列をランダムにシャッフルするという意味では多少結果が違っても問題ないのですが、自分は暗号化のライブラリを書いていた関係でseedを固定してもruby 1.9と2.0で Array#shuffle の結果が変わるというのはちょっと困りました。(1.9で暗号化したものが2.0で復号化できなくなるため)

しょうがないので、rubyのCの実装を見ながらrubyに再実装するというわけの分からないことをしましたw *1

とりあえずこれでRuby 1.9と2.0で同じ結果を返すようになりました。めでたしめでたし

1.9.3p429 :037 >  [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].my_shuffle(Random.new(445))
 => [8, 10, 6, 7, 5, 1, 3, 2, 9, 4]

2.0.0-p195 :038 >  [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].my_shuffle(Random.new(445))
 => [8, 10, 6, 7, 5, 1, 3, 2, 9, 4]

補足

配列をランダムにシャッフルするなら

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].sort{|a, b| random.rand(-1.0 .. +1.0)}

というのを考えると思いますが、これだと再現性はとれても結果に偏りが出てしまうのでダメです

参考URL: ランダムソート(笑)とは - 西尾泰和のはてなダイアリー

*1:ちなみに同時期にPythonC#のサンプルコードをrubyで書き直すということをやってたのでCは余裕だったw