PHPでモンテカルロ法を使って円周率を求める

(1/1)
シミュレーションの一種であるモンテカルロ法を使って円周率を推計するプログラムをPHPを使って作ってみる。同時に、モンテカルロ法の様子を画像として表示できるようにした。

(2026年4月5日)PHP8.5対応, pahooInputData.php導入

サンプル・プログラム:実行例

PHPでモンテカルロ法を使って円周率を求める

目次

サンプル・プログラム

圧縮ファイルの内容
montecarlo.phpサンプル・プログラム本体
pahooInputData.phpデータ入力に関わる関数群。
使い方は「数値入力とバリデーション」「文字入力とバリデーション」などを参照。include_path が通ったディレクトリに配置すること。
montecarlo.php 更新履歴
バージョン 更新日 内容
1.3.0 2026/04/05 PHP8.5対応, pahooInputData.php導入
1.2.0 2023/01/08 getRandomDot()改良--PHP8.2対応
1.1 2022/04/29 PHP8対応,リファラ・チェック改良
1.0 2019/12/07 初版
pahooInputData.php 更新履歴
バージョン 更新日 内容
2.0.1 2025/08/11 getParam() bug-fix
2.0.0 2025/08/11 pahooLoadEnv() 追加
1.9.0 2025/07/26 getParam() 引数に$trim追加
1.8.1 2025/03/15 validRegexPattern() debug
1.8.0 2024/11/12 validRegexPattern() 追加

モンテカルロ法とは

ある事象をモデル化した数式や関数があるとき、その定義域に含まれる値をランダムに大量生成して実際に計算し、得られた結果を統計処理することで、推計値が求まるという一種のシミュレーションである。
中性子の運動を探るためにアメリカの数学者スタニスワフ・ウラムが考案し、コンピュータの父フォン・ノイマンが、カジノで有名なモナコ公国の地区の名前をとって命名した。
PHPでモンテカルロ法を使って円周率を求める
モンテカルロ法で円周率を求めることができる。
正方形の紙に円が描かれていたとする。ここで、正方形の中にランダムに点を打ち、円の中に入っているかどうかで場合分けるとする。
PHPでモンテカルロ法を使って円周率を求める
一方、正方形の面積 $ A_S $ および 円の面積 $ A_C $ は $$ \begin{align*} A_S &= 4R^2 \tag{1} \\ A_C &= \pi R^2 \tag{2} \end{align*} $$
である。
正方形の中にランダムに打った点のうち、円の中に入った個数 $ N_{in} $ と入らなかった個数$ N_{out} $ の比は、面積の比になるから $$ \begin{align*} N_{in} : N_{out} = A_C : A_S - A_C \tag{3} \end{align*} $$
となる。
式3に式1,2を代入して変形すると、 $$ \displaystyle \begin{align*} \pi = \frac{4N_{in}}{N_{in} + N_{out}} \tag{4} \end{align*} $$
と円周率を求める式となる。

これをPHPプログラムで組んだのが "montecarlo.php" である。計算を簡単にするため、円を4分の1に切り取って行っている。打つ点の数を10万個以上にすると、だいぶ円周率に近くなってくる。

準備:初期値

montecarlo.php

  51: // TrueTypeフォント・ファイル;各自の環境に合わせて要変更
  52: define('FILE_FONT', '../../../../common/font/ipagp.ttf');
  53: 
  54: // 初期値
  55: define('COUNT',     100000);        // 試行回数
  56: define('WIDTH',     500);           // 直径(ドット)
  57: define('COLOR_IN',  'FF0000');      // 描画カラー:円周の内側
  58: define('COLOR_OUT', '0000FF');      // 描画カラー:円周の外側
  59: define('COLOR_STR', '00FF00');      // 描画カラー:円周率
  60: define('SIZE_STR',  30);            // 描画サイズ:円周率

上記の初期値は任意に変更が可能である。
FILE_FONT には、円周率の値を描画するために必要となるTrueTypeフォント・ファイルを、プログラムを配置したパスから見た相対パスで記述する。
描画カラーは、RGBの16進表記である。

ランダムな点座標

montecarlo.php

 114: /**
 115:  * ランダムな点座標を1つ返す
 116:  * @param   なし
 117:  * @return  array(float, float) 座標
 118: */
 119: function getRandomDot() {
 120:     // PHP8.2以上はRandom\Randomizerを使う
 121:     if (isPHPversion(8, 2)) {
 122:         $n = 100000000;
 123:         $random = new Random\Randomizer();
 124:         $x = (float)$random->getInt(0, $n) / $n;
 125:         $y = (float)$random->getInt(0, $n) / $n;
 126:         $random = NULL;
 127:     // PHP8.2未満はmt_randを使う
 128:     } else {
 129:         $x = mt_rand() / mt_getrandmax();
 130:         $y = mt_rand() / mt_getrandmax();
 131:     }
 132: 
 133:     return array($x, $y);
 134: }

ユーザー関数 getRandomDot は、ランダムな点座標(XY座標)を1つ返す。組み込み関数  mt_rand  と  mt_getrandmax  を利用し、0以上1未満の小数乱数を発生させている。

なお、乱数ジェネレーター問題を解決したPHP8.2以上では、Random\Randomizer クラスを使って乱数を求めるようにした。

ポイントを返す

montecarlo.php

 136: /**
 137:  * ポイントを返す
 138:  * @param   $image = 画像リソースID(省略時はピクセル描画しない)
 139:  * @return  int ポイント
 140: */
 141: function getPoint($image=NULL) {
 142:     list($x, $y) = getRandomDot();
 143:     $res = sqrt(($x * $x+ ($y * $y)) <1 ? 1 : 0;
 144: 
 145:     if ($image !NULL) {
 146:         $color = ($res == 1? COLOR_IN : COLOR_OUT;
 147:         $arr = color2rgb($color);
 148:         $color = imagecolorallocate($image, $arr[0], $arr[1], $arr[2]);
 149:         imagesetpixel($image, (int)($x * WIDTH), (int)(WIDTH - $y * WIDTH), $color);
 150:     }
 151:     return $res;
 152: }

ユーザー関数 getPoint は、原点からの距離が1以下なら1を、1より大きいなら0を返す。
また、引数に画像リソースIDが指定されていれば、キャンパスに点を描画する。

ポイントを集計する

montecarlo.php

 154: /**
 155:  * ポイントを集計する
 156:  * @param   int $n 試行回数
 157:  * @param   $image = 画像リソースID(省略時はピクセル描画しない)
 158:  * @return  int 集計ポイント
 159: */
 160: function sumPoints($n, $image=NULL) {
 161:     $res = 0;
 162:     for ($i = 0$i < $n$i++) {
 163:         $res +getPoint($image);
 164:     }
 165: 
 166:     return $res;
 167: }

ユーザー関数 sumPoints は、指定した試行回数だけ getPoint を繰り返し実行する。そして、返ってきたポイントを加算し、戻り値とする。

メイン・プログラム

montecarlo.php

 169: // メイン・プログラム ======================================================
 170: $n  = getParam('n', FALSE, COUNT);      // 試行回数
 171: 
 172: $image = @imagecreatetruecolor(WIDTH, WIDTH);
 173: imagesavealpha($image, TRUE);
 174: 
 175: // モンテカルロ法で円周率を求める
 176: $res = sprintf('%.4f', 4 * sumPoints($n, $image) / $n);
 177: 
 178: $arr = color2rgb(COLOR_STR);
 179: $color = imagecolorallocate($image, $arr[0], $arr[1], $arr[2]);
 180: $font = dirname(__FILE__. '/' . FILE_FONT;    // パスの通し方に注意
 181: imagettftext($image, SIZE_STR, 0, 10, SIZE_STR + 10, $color, $font, $res);
 182: 
 183: header('Content-type: image/png');      // MIMEはPNGで
 184: imagepng($image);                       // ブラウザ表示
 185: // PHP8.5:非推奨関数
 186: if (PHP_VERSION_ID < 80500) {
 187:     imagedestroy($image);               // 後処理

メイン・プログラムでは、モンテカルロ法で円周率を求め、変数 $res に格納する。
$res の値はキャンパスで描画するのだが、ここでフォント・ファイルへのパスの通し方に注意が必要だ。

参考サイト

(この項おわり)
header