概要
3月14日になると、コンビニやデパートがお菓子のセールスでにぎわっており、春も近づいてきたんだと感慨にふける人たちも多いと思います。
世間の皆さんもやはり円周率の日に注目を集めているわけで、経済効果もかなりあるのではないかと思ったりするわけです。
ですので、今日は円周率を計算するプログラムを書いてみます。
素朴なネタです。
ガウス・ルジャンドルのアルゴリズム
wikipediaのガウス=ルジャンドルのアルゴリズムによると、割と簡単に実装できます。
こんなの考えるなんて、すごい天才なんだと思いました。
python実装
といっても、自分は円周率の計算方法はよくわからないので、いろいろな人のソースを見たりして出来上がりました。
特徴としては、多数の桁数を表すため、Decimalを利用したことくらい。
# -*- coding:utf-8 -*-
import math
import time
from decimal import *
import numpy as np
#文字数1002
PI_1000 = "3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412737245870066063155881748815209209628292540917153643678925903600113305305488204665213841469519415116094330572703657595919530921861173819326117931051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798609437027705392171762931767523846748184676694051320005681271452635608277857713427577896091736371787214684409012249534301465495853710507922796892589235420199561121290219608640344181598136297747713099605187072113499999983729780499510597317328160963185950244594553469083026425223082533446850352619311881710100031378387528865875332083814206171776691473035982534904287554687311595628638823537875937519577818577805321712268066130019278766111959092164201989"
def get_pi(prec=1000, verbose=False):
'''
小数点以下の桁数がprecの円周率を文字列で返す
'''
prec = prec+1+1 #精度 "3."の部分があるので、1つ精度を増やして、さらにもう1けたないと丸目の関係でずれることがあるので、さらに1追加
N = 2*math.ceil(math.log2(prec)) #繰り返し回数 wikipediaによると、log2(prec)程度でいいらしいので、念の為、その2倍程度にする
getcontext().prec = prec
a = Decimal(1.0)
b = Decimal(1.0) / Decimal(2.0).sqrt()
t = Decimal(0.25)
p = Decimal(1.0)
start_time = time.clock()
# N回の試行を開始
for _ in range(1, N):
a_next = (a + b)/Decimal(2)
b_next = (a*b).sqrt()
t_next = t - p * (a - a_next)**2
p_next = Decimal(2)*p
a = a_next
b = b_next
t = t_next
p = p_next
#円周率を計算
pi = (a + b)**2 / (Decimal(4)*t)
#実行時間を計算
elapsed = time.clock() - start_time
# 実行結果を表示する
if verbose:
print("精度:",prec)
print("円周率:", pi)
print("実行時間:%f" % (elapsed))
return str(pi)[0:prec]
下のように実行して1000桁くらいは正しいことを確かめました。(2億桁くらい計算している人もいるそうですけど、これくらいでいいでしょ。)
pi = get_pi(prec=1000, verbose=True)
print(pi)
#print(PI_1000)
#print(pi == PI_1000)
精度: 1002
円周率: 3.14159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651328230664709384460955058223172535940812848111745028410
270193852110555964462294895493038196442881097566593344612847564823378678316527120190914564856692346034861045432664821339360726024914127372458700660631558817488152092096282925
409171536436789259036001133053054882046652138414695194151160943305727036575959195309218611738193261179310511854807446237996274956735188575272489122793818301194912983367336244
065664308602139494639522473719070217986094370277053921717629317675238467481846766940513200056812714526356082778577134275778960917363717872146844090122495343014654958537105079
227968925892354201995611212902196086403441815981362977477130996051870721134999999837297804995105973173281609631859502445945534690830264252230825334468503526193118817101000313
783875288658753320838142061717766914730359825349042875546873115956286388235378759375195778185778053217122680661300192787661119590921642019890
実行時間:0.014581
統計情報
統計学入門に、円周率に出てくる数字の出現率を調べる問題があったので、解いてみました。
グラフを見たあと、カイ2乗検定も実施して、数字が一定に出ているといえるか調べてみます。
123桁目から100個の数字をとってきたとき
123桁目というのは適当です。
start = 123
pi_123 = pi[2+start:2+start+100]
freq = np.zeros(10, dtype=np.int)
for a in pi_123:
freq[int(a)] += 1
print("freq(100)=",freq)
#chisquareを使って求める
print(chisquare(freq, 10*np.ones(10,dtype=np.int8)))
plt.xlim(-0.5, 9.5)
plt.bar(np.array([0,1,2,3,4,5,6,7,8,9]), freq, align='center')
グラフを見ると直感的には、気持ちが悪いけど、p値は0.37くらいなので、だいたい出現率は一定といっても矛盾はない。
freq(100)= [ 9 12 11 7 15 13 7 4 12 10]
Power_divergenceResult(statistic=9.8000000000000007, pvalue=0.3669177991127523)
1000桁の時
freq = np.zeros(10, dtype=np.int)
for a in pi:
if a != ".":
freq[int(a)] += 1
print("freq(1001)=",freq)
print(chisquare(freq, 100.1*np.ones(10,dtype=np.int8)))
plt.xlim(-0.5, 9.5)
plt.bar(np.array([0,1,2,3,4,5,6,7,8,9]), freq, align='center')
1000個くらいデータが集まると、出現率は数字に依らず一定と考えても矛盾はない。p値も0.85でいい感じ。
freq(1001)= [ 93 116 103 103 93 97 94 95 101 106]
Power_divergenceResult(statistic=4.7842157842157853, pvalue=0.85269838594638103)
他気になること
本当は、数字の並びの統計情報とかも知りたかったりしたけど、それは次ということで。
超越数とかいつか勉強しないとな。