猫の居る会社で有名なqnoteに入社させていただき、半年が経とうとしております。
最近では猫社員とも打ち解けてきており、モニターの前に陣取られてしまうこともしばしばです。
#目的
世間ではカジノ法案の採決で与野党が揉めているようですが、カジノといったらランダムの世界ですよね。今回はAndroidでモンテカルロ法を使用し円周率を求め、Mathクラスのrandom()メソッドで取得できる値に偏りがないか確認したいと思います。
#モンテカルロ法と円周率
モンテカルロ法をWikipediaで調べるとこんな感じに書かれております。
シミュレーションや数値計算を乱数を用いて行う手法の総称。
モンテカルロ法で円周率を求める場合は、XとYを半径以下の乱数として点を打ちます。一辺が半径の正方形内に分布し、これらの点の総数と円内の数の比が正方形と扇状の円の面積比になることを利用して円周率を求めます。
- 1.0×1.0の正方形にランダムで点を打つ
- 打たれた点の座標XとYをそれぞれ二乗し足す
- その数が1.0より小さければ扇状の円の中、大きければ外とする
- 全体に打った点の数と扇状の円の中に入った点の数を割り4倍すると円周率!
import android.content.Context;
import android.graphics.*;
import android.util.Log;
import android.view.View;
public class GraphicsView extends View {
Paint paint = new Paint();
int NUMBER = 1000000;
double[] randX, randY;
public GraphicsView(Context context) {
super(context);
randX = getRandom();
randY = getRandom();
}
private double[] getRandom() {
double[] rand = new double[NUMBER];
for (int i = 0; i < NUMBER; i++) {
rand[i] = Math.random();
}
return rand;
}
@Override
protected void onDraw(Canvas canvas) {
int n1 = 0;
double pi = 0.0;
for (int i = 0; i < NUMBER; i++) {
int intx0 = (int) (randX[i] * 500);
int inty0 = (int) (randY[i] * 500);
if (randX[i] * randX[i] + randY[i] * randY[i] < 1.0) { /* 円の中に入っているか */
n1 = n1 + 1;
paint.setColor(Color.rgb(255, 0, 0));
canvas.drawPoint(intx0, inty0, paint);
} else {
paint.setColor(Color.rgb(0, 0, 0));
canvas.drawPoint(intx0, inty0, paint);
}
pi = (double) n1 / (i + 1) * 4;
}
Log.v("LogPi", "円周率:"+pi);
}
}
実際に描画した画面(打点数10万)
この時の求めた円周率は3.134でした。
もしランダムメソッドから得られる値に偏りが合った場合、ここで求められる円周率が大幅に狂っていきます。
3.134という値は。。。いや駄目ですね。算数のテストで使用したら間違いなくバツですね。
モンテカルロ法はサンプル数が多いほど、ある程度まで精度が上がる性質があるので、次は300万回の打点を行ってみます。
実際に描画した画面(打点数300万)
求められた円周率は3.1412。。。3.14になったのでOKとしてください。モンテカルロ方は正確な値を求められる方法ではないのでこんな感じです。
#余談
randXとrandYの配列の他にrandZを用意し
if (randX[i] * randX[i] + randY[i] * randY[i] < 1.0) {
を
if (randX[i] * randX[i] + randY[i] * randY[i] + randZ[i] * randZ[i] < 1.0) {
とすると、先程までは2次元の円が描画されていましたが、
球体で描画されます。
同じ方法でもう一次元足して上げると4次元の球体が描画。。。されているのかな??