2008年3月11日火曜日

モンテカルロでパイ


モンテカルロシュミレーションにより円周率(パイ)を算出するプログラム。
”まぐれ”という不確実性科学の権威が書いた本を読んでいて簡単に
円周率を出せるというのでやってみた。

適当な円に密接する四角形を描く。ダーツゲームのようにその四角内に矢をあてる。
円の内部に入った矢と四角形内の矢すべての比率が円周率となる。
なんでかはわかりそうだったが、考えてたら頭がいたくなった・・・

とりあえず検証ということでrubyのTKでリアルタイムに描写させようとしたらはまった。
乱数発生の精度が低かった模様。最初は0〜100で乱数生成したので。
その後、精度を0〜10000にしたら良い数値が得られた。
3.14に近づくには100万回くらい待たなければだめみたい。


require "tkclass"
require "tk"

OFFSET=10

$oval=0
$sque=0
$disp_hankei=100
$hankei=10000

module MonteCarlo
def MonteCarlo.plot(max_x=$hankei*2, max_y=$hankei*2)
x = rand(max_x+1)
y = rand(max_y+1)
[x, y]
end
end

label = TkLabel.new do
text("0")
pack()
end

$c = TkCanvas.new('width'=>($disp_hankei+OFFSET)*2, 'height'=>($disp_hankei+OFFSET)*2).pack

Oval.new($c,OFFSET+0,OFFSET+0,OFFSET+$disp_hankei*2,OFFSET+$disp_hankei*2)
Rectangle.new($c,OFFSET+0,OFFSET+0,OFFSET+$disp_hankei*2,OFFSET+$disp_hankei*2)

def plot
x2 = 2
bx, by = MonteCarlo.plot($hankei*2, $hankei*2)
x, y = bx, by

x = bx - $hankei
y = by - $hankei

bx = bx / 100
by = by / 100

if (x*x + y*y) < $hankei*$hankei
$oval += 1
dot = Oval.new($c,OFFSET+bx,OFFSET+by,OFFSET+bx, OFFSET+by, "outline" => "red", "fill" => "red")
else
dot = Oval.new($c,OFFSET+bx,OFFSET+by,OFFSET+bx, OFFSET+by)
end
$sque += 1
end

count=0
TkAfter.new(1, -1, proc do
count+=1
plot
pai = $oval.to_f/$sque.to_f * 4.0
label.text("#{count.to_s} #{pai.to_s}")
end).start

Tk.mainloop


リアルタイムで描写しないのであれば乱数を少数で生成して円でなく扇がたで考え
シュミレーションするとあっというまによい精度でできた。
$oval = 0; $sque = 1

def plot
x, y = rand, rand
$oval += 1 if (x*x + y*y) < 1.0
$sque += 1
end

1000000.times {
plot
}

pai = $oval.to_f/$sque.to_f * 4.0
puts "pai #{pai.to_s}"


出力
pai 3.14324485675514


正直この本は難解な箇所が多々ありまが、理解できるとおもろいです。

干し芋のリスト