作者: dune
日時: 2002/5/06(02:38)
極悪です。長文かも。ネタです。悩んでなんかいません(笑)

Perl の乱数は怪しいじゃないか、という話の中で、モンテカルロ
でπを求める話が出てきました。これは正方形内に打ったランダム
な点のうち、内接している円内にあるものの割合を求めれば円の面
積が分かってπも求まるという話。このとき乱数が一様でないと正
確な結果が出ないわけです。

僕は以前試したことがありますが、乱数の種の選び方に問題がある
のかぜんぜん精度が出ませんでした。いくら時間をかけても有効桁
が伸びず、小数点以下4〜5桁くらいでフラフラしてたと思います。
そのときは「なんだこりゃ、これなら乱数ではなく等間隔の点のほ
うがマシかな」と思いつつ諦めました。

んで、今回話が出たので再度やってみました。

計算は半径1の円(の第一象限)で考えます:

    $pi = 4 * $p / $a;
    
        $p : 円の内部に落ちた点の数
        $a : 総点数(正方形内に落とした点の数)



まずは普通にモンテカルロ:

srand 1;
my $a;      # whole area
my $p;      # inner area
my $acc = 0;
my $n   = 1;
while($acc < 8){
    $n  += $n;
    my $da  = $n*$n/4;
    my $dp;
    foreach(1..$da){
        my $x   = rand;
        my $y   = rand;
        ++$dp if $x*$x+$y*$y <= 1;
    }
    my $pi  = ($p += $dp)/($a += $da)*4;
    printf "(%d) %12.10lf\n",
                $acc = length $p,$pi;
}

無用に複雑なことをしてますが、それは下の等間隔メッシュ版を先
に作ったから。



次に乱数を使わず、等間隔にメッシュを切った場合(今回はこれを
試したかった):

my $a;      # whole area
my $p;      # inner area
my $acc = 0;
my $n   = 1;
while($acc < 8){
    $n  += $n;
    my $rr  = $n*$n;
    my $da  = $rr/4;
    my $dp;
    for(my $ix = 1; $ix <= $n; $ix += 2){
        for(my $iy = 1; $iy <= $n; $iy += 2){
            ++$dp if $ix*$ix+$iy*$iy <= $rr;
        }
    }
    my $pi  = ($p += $dp)/($a += $da)*4;
    printf "(%d) %12.10lf \n",
                $acc = length $p,$pi;
}

メッシュを倍々で細かくしていったときのπの値を表示するように
なっています。やたら複雑になってる原因は、メッシュを倍にした
ときに追加分($dp,$da)だけを計算するようにしているため。終
了条件が length $p < 8 になってるのは、こうしとけば有効桁8
桁のπが求まるんじゃないか、という朝知恵。



さて結果ですが、両方とも五十歩百歩でした。

モンテカルロでは
%timer "perl mont_pi.pl"
(1) 4.0000000000
〜中略〜
(7) 3.1414205516
(8) 3.1412784329
36.521000 sec

等間隔メッシュでは
%timer "perl mesh_pi.pl"
(1) 4.0000000000
〜中略〜
(7) 3.1416251148
(8) 3.1416038743
23.577000 sec

やはりモンテカルロよりも等間隔メッシュのほうが精度は良い (*1)
ようですが、贔屓目に見ても5桁、とても目標の8桁まではいきませ
ん。乱数生成ルーチンをモジュールの Math::Random::MT (*2) で置
き換えてみましたが、

%timer "perl montMT_pi.pl"
(1) 0.0000000000
〜中略〜
(7) 3.1418346847
(8) 3.1416358820
207.684000 sec
%

となって、これもどんぐりの背比べです(というか時間かかりす
ぎ)。経験上、一晩ほっといてもこれ以上精度は出ないと思います。
スピードはともかく、理屈は合ってるはずなのに なぜ精度が出な
いのでしょうか。




(*1) だいたいこういう問題に乱数を使うのが間違ってる気がする。
乱数ってのは規則性が邪魔になる場合に使うものじゃないかな。
逆に言えば、規則的に打った点の位置を変調してから使えばモンテ
カルロで乱数を使わなくても済むと思う。

(*2) ActivePerl なら ppm でインストールできます。
>The Mersenne Twister is a pseudorandom number generator
>developed byMakoto Matsumoto and Takuji Nishimura. It is
>described in their paper at
><URL:http://www.math.keio.ac.jp~nisimura/random/doc/mt.ps>.
だそうな。
-- 
FZH01112@..., http://www1.u-netsurf.ne.jp/~dune/Perl_82_C5pi.html?