配列のシャッフルの決まり文句は「sort_by{ rand }」だった

配列要素のランダム抽出でRuby1.8.7をチェック - 技術メモ的なモノと気になるモノ

元のget_random_dataはエラーが出てたので修正。Ruby 1.8.6以前ではあれこれ考えるよりも「sort_by { rand }」するのが手っ取り早い。
だが、Ruby 1.8.7になるとそんなイディオム知らなくても「shuffle」と言えばいいだけ。意図がわかって文字数減って楽チン。

def get_random_data(data,max_size=nil)
  max_size = data.size unless max_size
  data_list = data.dup #sliceを使うのでコピー
  (1..max_size).map {|i|data_list.slice!(rand(data_list.size))}
end

def shuffle_n(data,n=nil)
  a = data.sort_by { rand }
  n ? a[0,n] : a
end

a = [1,2,3,4,5]
get_random_data a         # => [3, 5, 4, 2, 1]
get_random_data a,2       # => [3, 1]
shuffle_n a               # => [3, 4, 1, 2, 5]
shuffle_n a,2             # => [5, 2]

Ruby 1.8.7への差分は俺の挙げたリストだけでほとんど揃っているだろう。あとはバグ修正とか。

追記

すみっこ
ランダムソート(笑)とは - 西尾泰和のはてなダイアリー

ランダムソートの落とし穴については初知り。納得。

たしかに「比較関数に乱数を設定した」場合はばらつきが出る。

h = Hash.new(0)
7000.times do 
  h[(0..6).sort {rand(3)-1 }.first] += 1
end
h.sort                          # => [[0, 1983], [1, 587], [2, 621], [3, 494], [4, 1082], [5, 655], [6, 1578]]

しかし、sort_byはシュウォーツ変換によるものだ。乱数をN回発生させて、発生させた乱数を基準にsortする。だからほぼ均等になる。

h = Hash.new(0)
7000.times do 
  h[(0..6).sort_by { rand }.first] += 1
end
h.sort                          # => [[0, 997], [1, 1010], [2, 976], [3, 975], [4, 999], [5, 994], [6, 1049]]