Pythonでモンテカルロ法の実装し、円周率を計算する方法をソースコード付きでまとめました。
【モンテカルロ法】円周率の計算
モンテカルロ法とは、乱数を用いてシミュレーションや数値計算を行う手法の1つです。
モンテカルロ法で円周率を求める場合、以下の操作を行います。
| 手順 | 操作内容 |
|---|---|
| ① | 正方形(1×1)内に点をランダムで生成します。 |
| ② | 「生成した点」と「原点」の距離が1以下なら1ポイント、1より大きいなら0ポイントを与えます。 |
| ③ | 手順①②の操作をN回繰り返し、合計ポイントPを計算します。 |
| ④ | 合計ポイントPを4倍し、試行回数Nで割った値が円周率(近似値)となります。($\pi = \frac{4P}{N}$) |
| 原理詳細 | ■【モンテカルロ法とは】円周率を計算するアルゴリスム |
サンプルコード
サンプルプログラムのソースコードです。
# -*- coding: utf-8 -*-
import random
import time
def monte_method(N = 1000000):
point = 0
for i in range(N):
x = random.random()
y = random.random()
if x*x+y*y < 1.0:
point += 1
# 円周率の近似解
pi = 4.0 * point / N
return pi
start_time = time.clock()
# 試行回数
N = 10000000
# モンテカルロ法
pi = monte_method(N)
# 実行時間を計算
end_time = time.clock()
run_time = end_time - start_time
# 実行結果
print("試行回数:", N) # 試行回数: 10000000
print("実行時間:", run_time) # 3.1853431999999997[sec]
print("円周率:", pi) # 円周率: 3.1418236
【Matplotlib】グラフ化
続いて、Matplotlibモジュールでグラフ化してみましょう。
# -*- coding: utf-8 -*-
import random
import matplotlib.pyplot as plt
def monte_method(N = 1000000):
point = 0
for i in range(N):
# 乱数で点(x, y)をランダム生成
x = random.random()
y = random.random()
# 原点からの距離が1未満(円内部)なら
if x*x+y*y < 1.0:
# ポイント加算
point += 1
# 赤点をプロット
plt.plot(x, y,"ro")
# 距離が1以上(円外部)なら
else:
# 青点をプロット
plt.plot(x, y,"bo")
# 円周率の近似解を返す
return 4.0 * point / N
# 試行回数
N = 1000
# モンテカルロ法
pi = monte_method(N)
# 実行結果
print("試行回数:", N) # 試行回数: 100
print("円周率:", pi) # 円周率: 3.1418236
# 結果を表示
plt.grid(True)
plt.xlabel('X')
plt.ylabel('Y')
plt.show()

【NumPy】モンテカルロ法の高速化
先程の方法だと、for文を使っているため計算速度が遅くなります。
そこで、for文を使わずNumpy配列で一気に乱数の生成と個数のカウントを行います。
# -*- coding: utf-8 -*-
import random
import time
import numpy as np
def monte_method(N = 1000000):
x = np.random.rand(N)
y = np.random.rand(N)
# 距離が1未満の点の個数をカウント
point = np.sum(x*x+y*y < 1.0)
# 円周率の近似解
pi = 4.0 * point / N
return pi
start_time = time.clock()
# 試行回数
N = 10000000
# モンテカルロ法
pi = monte_method(N)
# 実行時間を計算
end_time = time.clock()
run_time = end_time - start_time
# 実行結果
print("試行回数:", N) # 試行回数: 10000000
print("実行時間:", run_time) # 0.3774597[sec]
print("円周率:", pi) # 円周率: 3.1416828
実行時間が1/10になり、大幅に高速化できました。
| - | 関連記事 |
|---|---|
| 1 | ■【NumPy入門】使い方・サンプル集 |
| 2 | ■【Python】標準モジュールで数値計算 |
| 3 | ■【Python入門】使い方とサンプル集 |

コメント