HTMLで円周率計算を可視化（モンテカルロ法）

今日は3月14日、πの日ということで円周率の計算をするプログラムを作ろう、という趣旨の記事です。

#使ったもの
HTMLとタイトルにありますが、HTMLの記述は数行でほとんどがJavaScriptです。HTMLのcanvas要素をJavaScriptでゴリゴリいじって描画します。
canvasについては、[このサイト](https://developer.mozilla.org/ja/docs/Web/HTML/Element/canvas)を参考にしました。

#計算方法
数値計算ではなく、確率（乱数）を用いる[モンテカルロ法](https://ja.wikipedia.org/wiki/%E3%83%A2%E3%83%B3%E3%83%86%E3%82%AB%E3%83%AB%E3%83%AD%E6%B3%95)で円周率を求めます。

![tanien.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/639929/21e9af07-8132-b7ee-8ca6-d9c9eb61b15d.png)
こんな感じの円と四角形に、ランダムに点を打ち込みます。

![pi.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/639929/6238aec7-82e9-ca92-39f8-940fafa1a732.png)

青色の点は円の内側、赤色の点は円の外側にある点です。
均一に点がばらまかれていれば、青と青＋赤の点の数の比は、円の面積と四角形の面積比と等しいはずです。
青色の点の数をa、赤色の点の数をbとすると、
$\pi r^2:4r^2 = a:a+b$
$(a+b)\pi r^2 = 4ar^2$
$\pi = \frac{4a}{a+b}$
となり、上記式を用いて円周率を求めることができる。

#実装
##HTML
canvas要素にidをつけて置いておくだけでいいです。
###HTMLのソースコード
```pi.html
<!DOCTYPE html>
<html>
    <head>
        <title>円周率</title>
        <script type="text/javascript" src="./pi.js"></script>
    </head>
    <body onload="init();">
        <h1>モンテカルロ法による円周率の計算</h1>
        <canvas id="pi"></canvas>
    </body>
</html>
```
##JavaScript
コードが長いので詳しい説明は省きます。今日中に終わりそうにないので。
ただ、やってることは、
1.背景の描画
2.乱数で点の生成
3.$\pi$の計算
4.2,3の繰り返し
と簡単なので、頭のいい人はもう少し短いコードで書けると思います。

ベクトルクラスを実装する際は[こちらの記事](https://qiita.com/Nekonecode/items/523a9e7214082129935e)を参考にさせて頂きました。
###JavaScriptのソースコード
```pi.js
//canvas要素の大きさ
const CANVAS_WIDTH = 400;
const CANVAS_HEIGHT = 400;

//変数の準備
var ctx;
const FPS = 1000;
const TIME = 1000/FPS;
var total = 0;
var In = 0;

//ベクトルクラスの実装
class vec2{
    //
    constructor(x=0, y=0){
        this.x = x;
        this.y = y;
    }

    //足し算メゾット
    add(v){
        this.x += v.x;
        this.y += v.y;
        return this;
    }

    //ベクトルを複製するメゾット
    clone(){
        return new vec2(this.x, this.y);
    }

    //静的足し算メゾット
    static add(v1, v2){
        return v1.clone().add(v2);
    }
}

//原点の位置を定義
var origin = new vec2((CANVAS_WIDTH/2),(CANVAS_HEIGHT/2));

//bodyが読み込まれたときに実行される関数
//
function init(){
    //canvas要素を取得
    let pi = document.getElementById("pi");
    if(pi.getContext){
        ctx = pi.getContext("2d");

        //canvasのサイズを指定
        pi.width = CANVAS_WIDTH;
        pi.height = CANVAS_HEIGHT;

        //背景の描画
        draw_init();

        //点を打ちまくる
        setInterval(draw,TIME);
    }
}

//繰り返し実行される関数
function draw(){
    //文字を消す処理
    ctx.clearRect(0,360,CANVAS_WIDTH,400);

    //乱数の代入と変数の準備
    let tempX = getRandomInt(200) - 100;
    let tempY = getRandomInt(200) - 100;
    let pi;

    //円の中か外の判定
    ctx.fillStyle = "rgb(255,0,0)"
    if((tempX**2)+(tempY**2) <= 100**2){
        In++;
        ctx.fillStyle = "rgb(0,0,255)"
    }
    total++;

    //piの計算
    pi = 4*In/total;

    //点の描画処理
    let v1 = new　vec2(tempX,tempY);
    v1.y *= -1;
    let screen = vec2.add(v1,origin);
    ctx.fillRect(screen.x,screen.y,1,1);


    //piとtotalの表示
    ctx.fillStyle = "rgb(0,0,0)"
    ctx.fillText(pi,0,380);
    ctx.fillText(total,0,400);
}

//背景を描画する関数
function draw_init(){
    //x軸
    ctx.beginPath();
    ctx.moveTo(0,CANVAS_HEIGHT/2);
    ctx.lineTo(CANVAS_WIDTH,CANVAS_HEIGHT/2);
    ctx.stroke();
    ctx.font = "20px serif";
    ctx.fillText("x",CANVAS_WIDTH-20,(CANVAS_HEIGHT/2)+20);

    //y軸
    ctx.beginPath();
    ctx.moveTo(CANVAS_WIDTH/2,CANVAS_HEIGHT);
    ctx.lineTo(CANVAS_WIDTH/2,0);
    ctx.stroke();
    ctx.fillText("y",(CANVAS_WIDTH/2)+10,15);

    //原点
    ctx.fillText("O",(CANVAS_WIDTH/2)-20,(CANVAS_HEIGHT/2)+20);

    //四角形
    ctx.beginPath();
    ctx.moveTo(100,100);
    ctx.lineTo(100,300);
    ctx.lineTo(300,300);
    ctx.lineTo(300,100);
    ctx.lineTo(100,100);
    ctx.stroke();

    //円
    let t;
    for(t=0;t<=2*Math.PI;t+=0.01){
        let v2 = new vec2(100,t);
        let v1 = new vec2(v2.x*Math.cos(v2.y),v2.x*Math.sin(v2.y));
        v1.y *= -1;
        let screen = vec2.add(v1, origin);
        ctx.fillRect(screen.x, screen.y, 1, 1);
    }
}

//乱数を返す関数
function getRandomInt(max) {
    return Math.floor(Math.random() * Math.floor(max+1));
}
```
##実行結果

![スクリーンショット 2021-03-14 午後11.07.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/639929/b29e73f1-c57a-ce0f-7fa1-b153eb4e9caa.png)

いい感じに近似出来ているのではないだろうか？

#あとがき的なもの
初めて記事を書きました。なので少し言い訳と予防線を貼ったりします。
マークダウンの書き方などわからないことが多く、見苦しい記事になっていたら申し訳ないです。
間違いなどがありましたら、コメントで指摘していただけたら修正いたします。

もうすぐ3月１４日が終わってしまう。はたして、3月１４日に見られた人はいるのだろうか。

追記（2021/03/16）
github.ioを使って公開しました。よかったら見てみてください。
[pi.html](http://tihanoro.github.io/pi/pi.html)

追記(2021/05/30)

```pi.js
//乱数を返す関数
function getRandomInt(max) {
    return Math.floor(Math.random() * Math.floor(max));
}
```

を

```pi.js
//乱数を返す関数
function getRandomInt(max) {
    return Math.floor(Math.random() * Math.floor(max+1));
}
```

と修正しました。