18
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

3月14日は円周率の日。pythonで円周率を計算した話

Last updated at Posted at 2017-03-14

概要

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)

freq_100.png

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)

freq_1000.png

他気になること

本当は、数字の並びの統計情報とかも知りたかったりしたけど、それは次ということで。
超越数とかいつか勉強しないとな。

18
14
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?