今回はプログラミングで円周率を求める方法について解説していきたいと思います!
あくまで求められるのは円周率の近似値になりますが、自分で円周率を求めることができるので特に数学が好きな方には楽しめると思います!
使用するプログラミング言語は Python ですが、円周率を求めるだけであればどのプログラミング言語でも求めることが可能です。
円周率の求め方
まずは円周率の求め方について整理していきたいと思います。
円周率と聞くととりあえず “3.14” を思い浮かべ、この “3.14” がどのような値であるかは意識しないことも多いのでは無いでしょうか?
使う分にはそれでいいよ!
今回はあえてその円周率をプログラムで求めてみる
プログラミングの力試しにもピッタリのテーマだよ
円周率の求め方はたくさんありますが、今回紹介する求め方はかなり直感的に理解しやすい方法になると思います。
円の面積
まずは円の面積を求める公式を思い浮かべてみましょう。
半径と円周率を用いて下記のようにして円の面積を求めることができます。
円の面積 = 半径 * 半径 * 円周率
これにより下の図の赤色部分の面積が求まります。
スポンサーリンク
一辺の長さを半径とした正方形の面積
ここで円の公式に出てきた「半径 * 半径」についてもう一段階掘り下げて考えてみましょう!
思い出していただきたいのが正方形の面積を求める公式です。
正方形の面積 = 一辺の長さ * 一辺の長さ
これより「半径 * 半径」は、一辺の長さを「半径」とした時の正方形の面積ということになりますね。
ですので、「半径 * 半径」は下の図の青色部分の面積となります。
つまり、上で示した円の公式は、
円の面積 = 半径 * 半径 * 円周率
一辺の長さを「半径」とした正方形の面積を用いて下記のように書き換えることができます。
円の面積 = 一辺の長さを円の半径とした正方形の面積 * 円周率
円周率の求め方
ここで、上の式を円周率について解くように整理してみましょう!
下記のように書き換えることができるはずです。
円周率 = 円の面積 / 一辺の長さを円の半径とした正方形の面積
これは「一辺の長さを円の半径とした正方形の面積」を1とした時の「円の面積」の大きさが「円周率」であることを示す式ですね!
つまり、円周率は下の図のように、2つの面積の比によって求めることができるというわけです!
ただし、「一辺の長さを円の半径とした正方形の面積」と「円の面積」の比と言われてもちょっと直感的には分かりにくいですね…。
もう少し直感的に分かるように、円に接する正方形について考えたいと思います。
この円に接する正方形は、一辺の長さを円の半径とした正方形が4つ集まった正方形ですので、円に接する正方形の面積は一辺の長さを円の半径とした正方形の面積の4倍になります。
ですので、円の面積と、その円に接する正方形の面積から下記のように求めることができます。
円周率 = 4 * 円の面積 / 円に接する正方形の面積
つまり、円に接する正方形の面積に対する円の面積の4倍の値が円周率であると言えます!
なるほど…
円周率って単純に 3.14 だと思い込んで使ってたけど、こんな感じで考えると面白いね!
そうだね!
しかもプログラミングができれば、この円周率をプログラムで求めることができるよ!
ここからはその方法について考えていこう!
円周率を求める
それでは円周率を求めていきましょう!
先ほど説明したように、円周率は下の式により求めることができます。
円周率 = 4 * 円の面積 / 円に接する正方形の面積
つまり「円の面積」と「円に接する正方形の面積」が分かれば、あとは上の式に当てはめて計算してやれば円周率が求まります。
簡単だね!
えーと、円周率が 3.14 だから…
え…。ちょっと待って?
円周率を求める式なのに円周率使って円の面積求めたら意味ないよね…
そうだね!
円周率を使わずに、いかにしてこの2つの面積の比を求めるかが円周率を求めるポイントだよ!
「円の面積」と「円に接する正方形の面積」の比を求めるのにどのような方法が思いつきますか?
多分方法はたくさんあります。数学の知識を使ってより精度高く比を求める方法もあると思いますが、今回は単純な下記の2つの方法で比を求めたいと思います。
- 均等に配置した点の数の比から求める
- ランダムに配置した点の数の比から求める(モンテカルロ法)
両方とも簡単なプログラムを作成することで、比を求めることができます!
スポンサーリンク
均等に配置した点の数の比から求める
1つ目に紹介する方法はこの「均等に配置した点の数の比から求める」方法です。
考え方
この考え方を図を用いて解説していきたいと思います。
まずこの方法でやることは、円に接する正方形を同じサイズの小さな正方形に分割することです(この分割した正方形を以降では “小正方形” と呼ばせていただきます)。
例えば下図は10 x 10の小正方形に分割した例になります。
で、ここで注目していただきたいのが各小正方形の左上の点です。
この左上の点は円に接する正方形内に「均等に離されて配置」されています。
そして、この点が中心を原点とした円の内側に存在するかどうかを判断します。円の内側に存在している場合、その点の小正方形を「円の一部」として考えます。
下の図であれば、赤色の点を左上とする小正方形が円の一部として考えられます。
10 x 10の小正方形に分割した場合、円の一部である小正方形を全て赤く塗りつぶしたものが下の図になります。
ちょっと形は歪ですが、赤色の円のように見えると思います。
この時、円の面積は下の式によって近似的に求めることができます。
円の面積 = 円の内側の点の数 * 小正方形のサイズ
さらに、円に接する正方形に関しては下記の式で求めることができます。
円に接する正方形の面積 = 全ての点の数 x 小正方形のサイズ
つまり、円周率は下記により計算することができます(分子分母共に存在する「小正方形のサイズ」は約分により消しています)。
円周率 = 4 * 円の内側の点の数 / 全ての点の数
まず1点目
円内に存在するかどうかはどうやって調べるの?
点が円内に存在するかどうかは、「円の中心」と「点」との距離によって判断することができます。
この距離はピタゴラスの定理によって求めることができます。
ピタゴラスの定理によると、直角三角形における斜辺の長さ c とその他の辺の長さ a と b の間には下記の等式が成立します。
c * c = a * a + b * b
これを「円の中心」と「点」に当てはめてみましょう。まず斜辺の長さ c は円の中心と点との距離になります。
さらに点の座標を (a, b) とし、円の中心座標を (i, j) とすれば各座標と斜辺の長さ c は下の図のような関係になります。
この時、ピタゴラスの定理より下記の式が成立することになります。
c * c = (a - i) * (a - i) + (b - j) * (b - j)
円の中心座標を (0, 0) とすれば、下記のようにピタゴラスの定理そのままで求められるので楽です。
c * c = a * a + b * b
これによって求まる距離(斜辺の長さ)が、円の半径以下であれば、円の内側に点が存在すると判断できますし、円の半径を超えるのであれば円の外側に存在すると判断することができます!
なるほど…
ピタゴラスの定理懐かしい!
じゃあもう1つの質問
さっき見せてくれた赤色の円、形が歪すぎるけどそれでも円周率は正しく求まるの?
いい質問だね!
正方形は小さく分割すればするほど(点の数が多ければ多いほど)、赤く塗った時の円の形が「より円に」近づくよ
なので、その分正確に円周率を求めることができるんだ
この円周率の求め方では正方形をより小さく分割した分、より精度の高い円周率を求めることができます。
これは、小さく分割したほうが、円の内側とみなした点の小正方形から構成される円の形がより円に近づくためです。
例えば 10 x 10 に分割した場合と、100 x 100 に分割した場合とでは円の形が下記のように変わります。
より円の形に近い 100 x 100 に分割した場合の方が、円周率がより正確に求められます。
ただし、分割すればするほどループの回数が増えるので、計算時間は増えることになります。
プログラム
解説が長くなってしまいましたが、ここで均等に配置した点の数の比から円周率を求めるプログラムを紹介したいと思います。
# -*- coding:utf-8 -*-
# 円の半径
RADIUS = 1000
# 円の内側の点の数
circle_num = 0
# 円に接する正方形内の点の数
square_num = 0
# 円に接する正方形内の全点 (x, y) に対してループ
for y in range(-RADIUS, RADIUS):
for x in range(-RADIUS, RADIUS):
# 必ず円に接する正方形内にはその点は存在する
square_num += 1
# 円の内側に点 (x, y) があるかを判断
if x * x + y * y <= RADIUS * RADIUS:
# 円の内側にある場合はカウントアップ
circle_num += 1
# 最後に点の数から円周率を計算
print(4 * circle_num / square_num)
円の半径は RADIUS
で設定しており、円に接する正方形を RADIUS * 2
x RADIUS * 2
個の小正方形に分割して円周率を求めています。
円の中心座標は (0, 0) として考えています。
RADIUS = 1000
の時の実行結果は下記のようになりました。
3.141547
実際の円周率は 3.14159265359… らしいので少数第4桁目までは正確に求められていますね!
さらに RADIUS
を大きくしてやれば精度は高くなると思いますが、かなり時間がかかってしまうと思います。
ランダムに配置した点の数の比から求める(モンテカルロ法)
次にもう一つの方法である「ランダムに配置した点の数の比から求める」について解説していきます。
考え方
基本的な考え方は、最初に紹介した「均等に配置した点の数の比から求める」と同じです。
円に接する正方形内に点を配置し、「円の内側に存在する点の数」と「円に接する正方形内の全ての点の数」の比によって円周率を求めます。
ただし、今回の方法では点の配置はランダムに行います。ここだけ「均等に配置した点の数の比から求める」と異なります。
円に接する正方形内にランダムに点を複数配置し、その配置した点の位置から「円の内側に存在する点の数」と「円に接する正方形内の全ての点の数」の比を求めます。
例えば上の図の場合はランダムに10個の点を配置した結果になります。
そして、「均等に配置した点の数の比から求める」と同様にその比から円周率を計算します。
円周率 = 円の内側の点の数 / 全ての点の数
上の図の例の場合、円の内側に存在する点は8つなので、円周率は下記の式で “3.2” として求まることになります。
円周率 = 4 * 8 / 10
円に接する正方形内の「どの位置にも等しい確率」で点がランダムに配置されるとすれば、円の内側には全ての点のうち「円の面積 / 円に接する正方形の面積」の割合で点が配置されるはずですので、これを利用して円周率を求めています。
ただし、ランダムに配置するので必ず点の配置には偏りが発生します。
この偏りは配置する点の数を多くすることで解消することができますので、より正確な円周率を求めるためには、より多くの点を配置する必要があります。
下記は私が配置する点を増やしながら、求まる円周率がどのように変化するかを実際に試してみた結果です。ちょっとずつ “3.14159…” に近づいている様子がわかると思います。
- 1:4.0
- 10:3.6
- 100:3.24
- 1000:3.152
- 10000:3.1376
- 100000:3.13272
- 1000000:3.141328
こんな感じで、乱数を用いてなんらかの値を見積もる方法を「モンテカルロ法」と呼びます。
「モンテカルロ法で円周率を求める問題」はプログラミングの学習でもよく取り上げられるテーマですので、この機会にしっかり理解しておきましょう!
プログラム
解説が長くなってしまいましたが、ここで均等に配置した点の数の比から円周率を求めるプログラムを紹介したいと思います。
# -*- coding:utf-8 -*-
import random
# 配置する点の数
NUM_POINT = 1000000
# 円の内側の点の数
circle_num = 0
# 円に接する正方形内の点の数
square_num = 0
# 配置する点の数分ループ
for i in range(NUM_POINT):
# 点を配置する座標をランダムに決定
x = random.uniform(-1, 1)
y = random.uniform(-1, 1)
# 必ず円に接する正方形内にはその点は存在する
square_num += 1
# 円の内側に点 (i, j) があるかを判断
if x * x + y * y <= 1 * 1:
# 円の内側にある場合はカウントアップ
circle_num += 1
# 最後に点の数から円周率を計算
print(4 * circle_num / square_num)
円の半径は 1
とし、-1
から 1
の間の乱数を2つ分、random.uniform 関数により発生させています。
そして、その発生した乱数を円に接する正方形内の座標とし、そこに点を配置する考え方でプログラムを組んでいます。
配置する点の数は NUM_POINT
で設定しており、数が多ければ多いほど円周率が正確に求められる可能性が上がります。
ただしその分ループ処理に時間がかかるので注意してください。
NUM_POINT=1000000
として実行した結果は下記のようになりました。
3.14282
割と円周率 “3.14159…” に近い値が求められていることが確認できると思います。
この方法のいいところは、点を配置すればするほど求めた円周率が本当の円周率に近づいていくところが確認できるところだと思います。
下記のスクリプトは tkinter を利用して点が配置されていく様子を可視化したものです。
# -*- coding:utf-8 -*-
import tkinter
import random
# 円の内側の点の数
circle_num = 0
# 円に接する正方形内の点の数
square_num = 0
# キャンバスのサイズ
WIDTH = 400
HEIGHT = 400
# 点の半径
RADIUS = 4
# 点を配置する関数
def place_dot():
global canvas, label_pi, label_num
global circle_num, square_num
# 点を配置する座標をランダムに決定
x = random.uniform(-1, 1)
y = random.uniform(-1, 1)
# 必ず円に接する正方形内にはその点は存在する
square_num += 1
# 円の内側に点 (i, j) があるかを判断
if x * x + y * y <= 1 * 1:
# 円の内側にある場合はカウントアップ
circle_num += 1
# 点の数から円周率を計算
pi = 4 * circle_num / square_num
# 以下画面に表示用の処理
# ラベルに結果を表示
label_pi.config(text='{:.5f}'.format(pi))
label_num.config(text=str(square_num))
# 円の内側かどうかで点の色を変える
if x * x + y * y <= 1 * 1:
fill_color="red"
else:
fill_color="lightgray"
# 描画する点の中心座標を計算(キャンバスの左上原点)
ox = 3 + x * WIDTH / 2 + WIDTH / 2 # 3は調整用
oy = 3 + y * HEIGHT / 2 + HEIGHT / 2 # 3は調整用
# キャンバスに点を描画
canvas.create_oval(
ox - RADIUS, oy - RADIUS, # 円の始点
ox + RADIUS, oy + RADIUS, # 円の終点
fill=fill_color,
outline="gray"
)
# 1ms 後に再度点を配置
canvas.after(1, place_dot)
# メインウィンドウ作成
app = tkinter.Tk()
# 点描画用のキャンバス作成
canvas = tkinter.Canvas(
app,
width=WIDTH,
height=HEIGHT,
)
canvas.grid(
row=0, column=0,
columnspan=2
)
# 円周率表示用のラベル作成
label_pi_text = tkinter.Label(
app,
text="円周率",
width=10,
font=("",20)
)
label_pi_text.grid(
row=1, column=0
)
label_pi = tkinter.Label(
app,
width=10,
font=("",20)
)
label_pi.grid(
row=1, column=1
)
# 配置した点の数表示用のラベル作成
label_num_text = tkinter.Label(
app,
text="点の数",
width=10,
font=("",20)
)
label_num_text.grid(
row=2, column=0
)
label_num = tkinter.Label(
app,
width=10,
font=("",20)
)
label_num.grid(
row=2, column=1
)
app.after(1, place_dot)
app.mainloop()
実行すれば正方形内にどんどん点が配置されていきます。
赤色の点は円の内側に配置された点で、灰色の点は円の外側に配置された点です。
これらの点の数から円周率を求め、画面の下側に表示しています。
点の数が増えるほど赤色で示される図形が円に近づき、円周率も 3.14159… に近づいていく様子がわかると思います。
配置する点が多くなるとアプリが重くなるので注意してください
当サイトではここで使用した tkinter についても解説していますので、こちらに興味のある方はぜひ下記カテゴリページも見てみてください!
https://daeudaeu.com/category/programming/python/tkinter/tkinter_tutorial/
まとめ
今回はプログラミングで円周率を求める方法について解説しました。
円周率 = 3.14 とだけ覚えている方も多いと思いますが、円周率はこんな感じでプログラミングにより求めることができます。
特に数学とプログラミングは相性が良いので、数学で習ったことを生かしてプログラミングしてみると、数学がより楽しくなりますし、プログラミングも楽しめますのでオススメです!