2017/04/06(木) [n年前の日記]
#4 [ruby] 正規乱数とやらが作れそうかテスト
_乱数にコクを出すアレ
が気になり始めたので Ruby + DXRuby を使ってテスト。
_「ちゃんとした正規乱数が欲しいなら Box-Muller を使うべき」 _「sin,cosとか平方根とか今時はたいして重くないし」 という主張も見かけたので、以下を参考にして Box-Muller もテスト。
_Box-Muller法による正規分布列生成 - Qiita
こんな感じのソースでどうだろう。合ってるのだろうか。
_boxmuller.rb
結果はこんな感じに。
上から、只の rand (= 一様分布)、Box-Muller法、乱数にコクを出すアレ。
ゲーム等で使う分には乱数にコクを出すソレでもそこそこ十分っぽい雰囲気。Box-Mullerはどこからどこまでの範囲を取るのかもちと分からんかったし…。
その後、 _「一様乱数の平均値を正規乱数として代用する」という話をゆるふわ統計的に検証する - k11i.biz で、 _Ziggurat法 なるものもあると知った。コレも試せば良かったかしらん。
_「ちゃんとした正規乱数が欲しいなら Box-Muller を使うべき」 _「sin,cosとか平方根とか今時はたいして重くないし」 という主張も見かけたので、以下を参考にして Box-Muller もテスト。
_Box-Muller法による正規分布列生成 - Qiita
こんな感じのソースでどうだろう。合ってるのだろうか。
_boxmuller.rb
# Box-Muller法による正規乱数が作れそうかどうかをテスト require 'dxruby' # 乱数の結果を DXRuby の Image に描き込む def get_image(w, h, data) # 最大カウント数を取得 m = data.max # Imageに描き込み img = Image.new(w, h) w.times do |x| cnt = (data[x] * h.to_f / m).to_i y = h - 1 cnt.times do img[x, y] = C_WHITE y -= 1 end end return img end # Box-Muller法で乱数を取得 def get_box_mular(mu, sigma) x = rand y = rand r = Math.sqrt(-2.0 * Math.log(x)) * Math.cos(2.0 * Math::PI * y) return r * sigma + mu end w, h = 1280, 720 hh = h / 3 tc = 200000 # 一様分布になってるか確認 n = Array.new(w, 0) tc.times do r = (rand * w).to_i n[r] += 1 end nml_img = get_image(w, hh, n) # Box-Muller法 n = Array.new(w, 0) tc.times do rbm = get_box_mular(0.0, 1.0) r = (rbm * (w / 8) + (w / 2)).to_i n[r] += 1 if r >= 0 and r < w end bmr_img = get_image(w, hh, n) # 乱数にコクを出すアレ n = Array.new(w, 0) tc.times do r = 0 cnt = 4 cnt.times { r += rand } r = ((r / cnt) * w).to_i n[r] += 1 end kok_img = get_image(w, hh, n) # 結果表示 Window.resize(w, h) Window.loop do break if Input.keyPush?(K_ESCAPE) Window.draw(0, 0, nml_img) Window.draw(0, hh, bmr_img) Window.draw(0, hh + hh, kok_img) end
結果はこんな感じに。
上から、只の rand (= 一様分布)、Box-Muller法、乱数にコクを出すアレ。
ゲーム等で使う分には乱数にコクを出すソレでもそこそこ十分っぽい雰囲気。Box-Mullerはどこからどこまでの範囲を取るのかもちと分からんかったし…。
その後、 _「一様乱数の平均値を正規乱数として代用する」という話をゆるふわ統計的に検証する - k11i.biz で、 _Ziggurat法 なるものもあると知った。コレも試せば良かったかしらん。
[ ツッコむ ]
以上です。