円周率πとは何か?幾何・級数・無理数・計算で徹底解説
円周率 は、中学で出てくる定数の中でも特に不思議な存在です。
と無限に続きますが、次の疑問にははっきりした数学的な答えがあります。
- なぜ は 3.14 付近なのか
- なぜ分数で表せないのか
- なぜ終わらないのに超高桁まで計算できるのか
この記事では、古代の幾何学から現代のアルゴリズムまで、 1本の流れとして整理します。
1. 幾何学的に見る:なぜ「3.14」近辺なのか
円周率は、円周の長さ を直径 で割った比です。
ここでは計算を簡単にするため、直径を とします。 このとき円周の長さそのものが です。
問題は、円周が曲線なので「正確な長さ」を直接測りにくいことです。 そこでアルキメデスは、円周を次の2つで挟む方法を使いました。
- 内接正多角形の周長(円周より短い)
- 外接正多角形の周長(円周より長い)
この2本のはさみで、 の範囲を少しずつ狭めます。
1.1 内接正六角形で下から抑える
まず半径 の円を考えます。 この円に内接する正六角形では、1辺の長さが半径と等しくなります。

したがって内接正六角形の周長は
です。円周はこの六角形より外側を通るため
が分かります。
1.2 外接正六角形で上から抑える
次に同じ円に外接する正六角形を見ます。 このとき1辺は、正三角形の性質を使うと手早く出せます。 外接正六角形の中心から見ると各辺に対応する角は で、 辺を二等分すると「30°-60°-90°」の直角三角形になります。 その辺の比()と半径 を使うと、半辺は 。 したがって1辺は
です(図の「一辺 」)。

よって外接正六角形の周長は
となります。円周はこの六角形より内側にあるため
です。
以上より、円周率は
と「上から・下から」挟めます。 この時点で、 が3より大きく3.5未満であることが図形だけで確定します。
1.3 角数を増やすと 3.14 に収束する
ここからがアルキメデスの核心です。 正六角形で終わらず、角数を
と倍々に増やして、内接・外接それぞれの周長を更新しました。
角数が増えるほど多角形は円に近づき、 下からの値と上からの値の差が小さくなります。
最終的に
を得ています。小数に直すと
です。つまり「3.14」は感覚的な近似ではなく、 図形による厳密なはさみうちで保証された値です。
2. 解析学的に見る:無限の足し算でπを作る
17世紀以降は、円を描かずに数式だけで を扱う方法が発展します。 代表はライプニッツ級数です。
奇数分母の逆数を交互に足し引きすると に近づきます。
2.1 なぜ奇数が出るのか
この式は、次の展開から出ます。
ここに を代入すると
なので、ライプニッツ級数が得られます。
つまり「円の角度情報」が「無限級数」に翻訳されているわけです。
2.2 この級数が遅い理由
この級数は正しいですが、実用上はかなり遅く収束します。 「遅い」とは、たくさん項を足しても正しい桁がなかなか増えない、という意味です。
第 項までの和を とすると
です。 この級数は「プラス・マイナスが交互」で、しかも項の大きさが と小さくなっていく交代級数です。
そのため部分和 は、真の値 を 「行き過ぎる・戻り過ぎる」を交互に繰り返しながら近づきます。 このとき誤差は、次の1項の大きさを超えません。
次の項の大きさは なので、
です。
両辺を4倍すると、 そのものの誤差は
と評価できます。
ここで重要なのは、誤差がだいたい「 のオーダー」でしか減らないことです。
つまり、精度を1桁上げるには、項数をほぼ10倍に増やす必要があります。
実際に計算すると次のようになります( を 近似値として比較)。
- :、誤差
- :、誤差
- :、誤差
- :、誤差
100万項まで計算しても、誤差は 程度です。つまり、の小数第五桁程度までしか正確にわかりません。 このため、超高桁の計算には向きません。
次の図は と低次近似の比較です。 中心付近( 近く)ではよく一致しますが、 に近づくほどズレが目立ちます。 ライプニッツ級数は を代入して使うので、ここでの近似の悪さが そのまま収束の遅さとして現れます。
3. 数論的に見る:なぜ小数は終わらないのか
2章では「どう計算するか」を見ました。 ここでは「なぜ終わらないのか」を、無理数の話とつなげて整理します。
3.1 先に結論:πは無理数
結論は次の1行です。
つまり
で、整数の比では表せません。 この事実だけで、「小数が終わらないこと」の本質がかなり見えてきます。
3.2 有理数と無理数の違い
数を小数表示で見ると、次の対応があります。
- 有理数(分数で書ける):有限小数か循環小数
- 無理数(分数で書けない):無限に続き、循環しない
具体例で見ると、
- (有限小数)
- (循環小数)
- (無限非循環小数)
- (無限非循環小数)
という分類になります。
3.3 なぜ分数は「有限か循環」になるのか
これは割り算の仕組みで説明できます。 分数の割り算では、毎回「余り」が出ますが、余りの候補は有限個しかありません。
たとえば分母が なら、余りは
のどれかです。 したがって、計算を続けると必ず
- 余りが になる(そこで有限小数として終わる)
- 以前出た余りが再登場する(そこから同じ桁が循環する)
のどちらかになります。
この「余りの再登場」が、循環小数の正体です。
3.4 無理数であることの証明イメージ
ここは「厳密証明の全文」を追うより、流れを掴むのが大事です。 方針は背理法で、次の形です。
- まず が有理数だと仮定する
- その仮定から、ある量は「無理数でなければならない」と言える
- しかし同じ量が「有理数」だと分かる
- 矛盾なので、最初の仮定( が有理数)を否定する
ランベルト(1761)の証明では、この「ある量」に を使います。 証明の核だけ書くと、
- が有理数なら、 は無理数になる
という性質を示し、そこに
を代入して考えます。 もし が有理数なら も有理数なので、上の性質から は無理数のはずです。
一方で
で、 は有理数です。ここで矛盾が出ます。 したがって「 が有理数」という仮定は誤りで、 は無理数です。
この証明イメージのポイントは、 「計算で桁を眺める」のではなく、 「仮定を置いて論理の矛盾を作る」ことで性質を確定しているところです。
3.5 「規則性がない」の正確な意味
ここは誤解しやすいので分けておきます。
- 証明済み: は循環小数にならない(単純な繰り返し規則はない)
- 未解決:桁の並びが統計的に完全ランダムかどうか
つまり、「循環しない」は確定していますが、 「完全にランダムだ」とまでは現時点で証明されていません。
3.6 3章まとめ
2章で見たように、級数を使えば を近似できます。 3章で見たのは、その先に「終点がない理由」です。
要点は1つで、 が無限に続くのは、分数で表せない(無理数)ことの必然です。
次の4章では、この終点のない数に対して、どうやって高速に桁を確定していくかを見ます。
4. 計算科学で見る:なぜ100兆桁まで計算できるのか
3章で「なぜ終わらないか」を見ました。 では、終わらない数をどうやって大量の桁まで計算できるのでしょうか。
4.1 まず復習:ライプニッツ級数は遅い
2章の誤差評価より、ライプニッツ級数の近似では
でした。つまり誤差はだいたい の速度でしか減りません。 このため、6桁精度(誤差 )を狙うだけでも、概算で約200万項が必要です。
4.2 高速計算の主役:チュドノフスキー法
現代の超高桁計算で有名なのが、次のチュドノフスキー法です。
この式が速い理由は、分母の が非常に大きくなることです。 が1増えるだけで項が急激に小さくなり、1項あたり約14桁分ずつ精度が増えるのが目安です。
4.3 具体比較:同じ精度まで何項必要か
ざっくり比較すると、次のような差になります。
| 目標精度 | ライプニッツ級数 | チュドノフスキー法 |
|---|---|---|
| 小数6桁程度 | 約200万項 | 1項程度 |
| 小数100桁程度 | 約 項規模 | 8項程度 |
| 小数1000桁程度 | 約 項規模 | 72項程度 |
「徒歩」と「高速鉄道」くらい、収束速度に差があります。
4.4 計算の速さは式だけでは決まらない
ここからは「紙とペン」ではなく、 プログラムで円周率を計算する場面を想定します。 実際の処理は、ざっくり次の流れです。
- 必要桁数を決める
- その桁数に足りる項数まで級数を計算する
- 最後に値を合成して出力する
このとき、式の収束速度だけでなく 「各ステップをどう実装するか」で体感速度が大きく変わります。
- 多倍長演算:普通の
double(プログラムで使う型)では15桁前後しか扱えないため、1000桁や1万桁では桁が壊れます。専用の整数・小数ライブラリで桁落ちを防ぎます。 - binary splitting:級数を左から順に足すと、巨大な分子分母の計算を何度も繰り返して遅くなります。和を木構造でまとめると、同じ結果をより少ない計算で作れます。
- 並列化:1コアで順番に計算するより、複数コアで部分計算を同時に進めて最後に合成した方が速くなります。
要するに、 「速く収束する式」だけでは不十分で、 「桁を壊さず・重複計算を減らし・同時実行する実装」がそろって初めて高速になります。
4.5 実行して挑戦:目標桁に到達するまで計算する(2方式比較)
ここでは、次の2つの方法を同じ目標桁で実行し、 どれくらいの速さで正しい桁に到達するかを比較します。
- ライプニッツ級数
- チュドノフスキー法
以下のPythonスクリプトを実行すると、対話形式で目標とする桁数を設定し、それぞれのアルゴリズムが「どれくらいの速さで正確な円周率の桁を導き出せるのか」を目の当たりにすることができます。
次のスクリプトは、以下に対応しています。
- 対話型のパラメータ設定: 各公式の目標桁数や、画面に表示する桁数を自由に指定できます。
- リアルタイムな進捗レポート: 計算中は10秒ごとに、現在の処理状況(経過時間・計算した項数・確定した桁数)を表示します。
- 安全なスキップ機能: 処理に時間がかかりすぎる場合、
Ctrl + Cを押せばエラーにならず、その方式の計算だけを中断して次のステップへ進めます。
コードを表示する(クリックで開く)
import time
from decimal import Decimal, localcontext
A = 13591409
B = 545140134
C = 640320
C3_OVER_24 = 10939058860032000
PROGRESS_INTERVAL = 10.0
def input_int(msg: str, default: int, min_value: int = 1) -> int:
raw = input(f"{msg} [{default}]: ").strip()
if raw == "":
return default
v = int(raw)
return max(v, min_value)
def guaranteed_digits_leibniz(terms: int) -> int:
if terms <= 0:
return 0
bound = Decimal(4) / Decimal(2 * terms + 1)
if bound >= 1:
return 0
with localcontext() as ctx:
ctx.prec = 50
d = int((-bound.log10()).to_integral_value(rounding='ROUND_FLOOR'))
return max(0, d - 1)
def estimated_digits_chudnovsky(terms: int) -> int:
return max(0, 14 * (terms - 1) - 2)
def run_leibniz(target: int):
print("\n=== ライプニッツの公式 ===")
print("計算を開始します...")
with localcontext() as ctx:
ctx.prec = target + 30
s = Decimal(0)
sign = 1
k = 0
start = time.perf_counter()
last = start
try:
while True:
s += Decimal(sign) / Decimal(2 * k + 1)
sign = -sign
k += 1
if k % 5000 == 0:
now = time.perf_counter()
g = guaranteed_digits_leibniz(k)
if g >= target:
pi_est = Decimal(4) * s
return pi_est, k, now - start, g
if now - last >= PROGRESS_INTERVAL:
print(f"[Leibniz] 時間={now-start:.1f}s 項数={k} 確定桁数={g}")
last = now
except KeyboardInterrupt:
now = time.perf_counter()
g = guaranteed_digits_leibniz(k)
pi_est = Decimal(4) * s
print("\n[Leibniz] ユーザーによって中断されました")
return pi_est, k, now - start, g
def run_chudnovsky(target: int, prec: int):
print("\n=== チュドノフスキーの公式 ===")
print("計算を開始します...")
with localcontext() as ctx:
ctx.prec = prec
D = Decimal
c_const = D(426880) * D(10005).sqrt()
m = D(1)
l = D(A)
x = D(1)
s = D(l)
k = 1
start = time.perf_counter()
last = start
try:
while True:
pi_est = c_const / s
now = time.perf_counter()
g = estimated_digits_chudnovsky(k)
if now - last >= PROGRESS_INTERVAL:
print(f"[Chudnovsky] 時間={now-start:.1f}s 項数={k} 確定桁数={g}")
last = now
if g >= target:
return pi_est, k, now - start, g
m = m * D((6 * k - 5) * (2 * k - 1) * (6 * k - 1)) / D(k * k * k)
l += B
x *= -D(C3_OVER_24)
s += m * l / x
k += 1
except KeyboardInterrupt:
now = time.perf_counter()
pi_est = c_const / s
g = estimated_digits_chudnovsky(k)
print("\n[Chudnovsky] ユーザーによって中断されました")
return pi_est, k, now - start, g
def main():
print("=== 円周率 計算アルゴリズム2種比較 ===")
print("Ctrl + C でいつでも計算を中断できます")
target_leib = input_int("Leibniz 目標計算桁数", 20, 1)
target_chud = input_int("Chudnovsky 目標計算桁数", 50, 1)
preview = input_int("プレビュー表示桁数", 80, 10)
prec = max(target_leib, target_chud) + 30
print("\n--- 設定内容 ---")
print(f"Leibniz 目標桁数 : {target_leib}")
print(f"Chudnovsky 目標桁数 : {target_chud}")
print(f"Chudnovsky 内部精度 : {prec}")
print("----------------")
leib, leib_terms, leib_time, leib_digits = run_leibniz(target_leib)
chud, chud_terms, chud_time, chud_digits = run_chudnovsky(target_chud, prec)
print("\n=== 計算結果 ===")
print(f"[Leibniz] 目標={target_leib}, 項数={leib_terms}, 時間={leib_time:.2f}s, 確定桁数={leib_digits}")
print(f"[Chudnovsky] 目標={target_chud}, 項数={chud_terms}, 時間={chud_time:.2f}s, 確定桁数={chud_digits}")
print(f"\nLeibniz 最初の{preview}桁:")
print(format(leib, f".{preview}f")[:preview + 2])
print(f"\nChudnovsky 最初の{preview}桁:")
print(format(chud, f".{preview}f")[:preview + 2])
with open("pi_compare_result.txt", "w", encoding="utf-8") as f:
f.write("=== 円周率 計算アルゴリズム2種比較 ===\n")
f.write(f"Leibniz 目標桁数={target_leib}\n")
f.write(f"Chudnovsky 目標桁数={target_chud}\n")
f.write(f"Leibniz: 項数={leib_terms}, 時間={leib_time:.2f}s, 確定桁数={leib_digits}\n")
f.write(f"Chudnovsky: 項数={chud_terms}, 時間={chud_time:.2f}s, 確定桁数={chud_digits}\n")
f.write("\n=== 確定した桁までの円周率 ===\n")
f.write("[Leibniz]\n")
if leib_digits > 0:
f.write(f"{format(leib, f'.{leib_digits}f')[:leib_digits + 2]}\n")
else:
f.write(f"{format(leib, '.5f')}... (確定桁なし)\n")
f.write("\n[Chudnovsky]\n")
if chud_digits > 0:
f.write(f"{format(chud, f'.{chud_digits}f')[:chud_digits + 2]}\n")
else:
f.write(f"{format(chud, '.5f')}... (確定桁なし)\n")
print("\n結果を pi_compare_result.txt に保存しました。")
if __name__ == "__main__":
main()
実行手順:
- 上のコードを
pi_compare.pyとして保存する - ターミナル(またはコマンドプロンプト)を開く
- そのファイルがあるフォルダに移動する
- 次を実行する
python pi_compare.py
python が通らない場合は次を試してください。
py pi_compare.py
実行ログも折りたたんで置いておきます。
実際の実行結果を表示する(クリックで開く)
=== 円周率 計算アルゴリズム2種比較 ===
Leibniz 目標計算桁数 [20]: 7
Chudnovsky 目標計算桁数 [50]: 1000
プレビュー表示桁数 [80]: 10
--- 設定内容 ---
Leibniz 目標桁数 : 7
Chudnovsky 目標桁数 : 1000
Chudnovsky 内部精度 : 1030
----------------
=== ライプニッツの公式 ===
計算を開始します...
[Leibniz] 時間=10.0s 項数=28465000 確定桁数=6
[Leibniz] 時間=20.0s 項数=56915000 確定桁数=6
[Leibniz] 時間=30.0s 項数=85205000 確定桁数=6
[Leibniz] 時間=40.0s 項数=112055000 確定桁数=6
[Leibniz] 時間=50.0s 項数=137490000 確定桁数=6
[Leibniz] 時間=60.0s 項数=162165000 確定桁数=6
[Leibniz] 時間=70.0s 項数=187300000 確定桁数=6
=== チュドノフスキーの公式 ===
計算を開始します...
=== 計算結果 ===
[Leibniz] 目標=7, 項数=200000000, 時間=75.68s, 確定桁数=7
[Chudnovsky] 目標=1000, 項数=73, 時間=0.01s, 確定桁数=1006
Leibniz 最初の10桁:
3.1415926486
Chudnovsky 最初の10桁:
3.1415926536
結果を pi_compare_result.txt に保存しました。
実行結果を見ると、ライプニッツ級数とチュドノフスキー法の収束の速さの違いが一目でわかりますね。
ライプニッツ級数は7桁確定させるために一分強かかったのに対し、チュドノフスキー法は1000桁を0.01秒で確定させています。 試してもらえばわかるでしょうが、もしこのプログラムを使ってライプニッツ級数で8桁を確定させようとしたら、十分単位で時間がかかるでしょう。
項数についても面白いですね。ライプニッツ級数は7桁確定させるために2億回の足し算を行っていることがわかります。ライプニッツ級数が発見されたのは17世紀ごろであり、その頃はもちろんコンピューターなどは存在しなかったため、当時の円周率を計算しようとする試みは途方もないことだったと予想できます。
4.6 なぜ円周率計算がベンチマークになるのか
円周率計算は数学の話題であると同時に、計算機の総合テストでもあります。
- 多倍長ライブラリの正しさ検証
- CPU・メモリ・ストレージの長時間安定性確認
- 並列実装の再現性チェック
このため、研究用途でも実務用途でも価値があります。
5. まとめ
円周率 は、次の4つの視点で理解できます。
- 幾何学:内接・外接多角形で 3.14 付近に挟める
- 解析学:無限級数で表せるが、式ごとに収束速度が違う
- 数論: は無理数なので小数は終わらない
- 計算科学:高速収束法で超高桁計算が可能になる
身近な「円」から、極限・級数・無理数・アルゴリズムまでつながる。 これが円周率の面白さです。