伊藤と申します。 rand のマニュアルに関して気付いたことですが、 日本語版マニュアルには |rand(max) | 0からmaxを越えない範囲の整数の乱数を発生します (maxは正の整数). とあり、英語版には、 |rand(max) | Returns a random integer number greater than or equal to 0 and less | than the value of max. (max should be positive.) とありますが、これは当然英語版のほうが正しいですね。 さて、rand の仕様がそうなっているとすると、Ruby FAQ の |7.3 0から51の中から重複のない5つをランダムに選ぶにはどうしますか | |次のメソッドは,0からnまでの数の中からm個をランダムに選んだ配列を返します. | | def sample(n, m) | if m.zero? | [] | else | s = sample(n-1, m-1) | t = rand(n+1) | s.concat s.include?(t) ? n : t | end | end は、次のようにしたほうが、sample(n,m) が rand(n) の自然な拡張に なって美しいと思います(単に好みの問題ですが)。 |7.3 0から51の中から重複のない5つをランダムに選ぶにはどうしますか | |次のメソッドは,0からn-1までのn個の数の中からm個をランダムに選んだ配列を |返します. | | def sample(n, m) | if m.zero? | [] | else | s = sample(n-1, m-1) | t = rand(n) | s.concat s.include?(t) ? n-1 : t | end | end しかし、なぜこのアルゴリズムが正しいのか疑問に思ったので、 次のようなプログラムを書いて検証してみました。 # 上のアルゴリズムで n個の数から m個の数を選ぶとき、 # n個の数それぞれが選ばれる確率の配列を求める。 def sample_p(n, m) if m == 1 a = [ (1.0/(n.to_f)) ] a *= n else a0 = sample_p(n-1, m-1) a = [] # i (0<=i<=n-2) が選ばれるのは、すでに選ばれているか、あるいは # これまで選ばれておらずに今回 rand(n) で選ばれる場合である。 (0..n-2).each{|i| a << (a0[i]+(1.0-a0[i])*(1.0/(n.to_f)))} # n-1 が選ばれるのは、今回 rand(n) で選ばれるか、あるいは、 # すでに選ばれている i (0<=i<=n-2) が今回も選ばれる場合である。 p = 1.0/(n.to_f) (0..n-2).each{|i| p += a0[i]*(1.0/(n.to_f)) } a << p end end p sample_p(20,8) p sample_p(100,13) やってみると、もののみごとに、すべての確率は m/n となりますが、 それが一般に正しいことは、数学的帰納法で容易に証明できますね。 しかし、最初にアルゴリズムを考えた人は賢い。 ******************** Ito Kazumitsu <ito / htk.hitachi-cable.co.jp>