跳轉到

前230年 · 阿基米德窮竭法:圓周率緝拿案#

卷宗編號:Case-0230-Exhaustion|偵探:阿基米德(Archimedes)|地點:敘拉古

案發現場#

西元前三世紀的地中海世界,藏著一樁懸了數百年的奇案:圓,到底有多大。

埃及人說圓面積是直徑的八分之九再平方,巴比倫人取圓周率為 \(3\) ,《聖經》裡的銅海也暗示同一個粗糙數字。商人要算糧倉容積,工匠要造圓柱神廟,天文學家要量天體軌道,可是誰也說不清,圓周與直徑之比究竟是個什麼數。

更棘手的還有第二現場:敘拉古王懷疑金匠在王冠裡摻了銀。國王要真相,卻不准把王冠熔掉。一個是無限曲線的謎,一個是不許破壞證物的謎。兩樁案子,看似無關,卻指向同一個兇手——連續與無限。

阿基米德接下委託時,希臘幾何學只有直尺、圓規與歐多克索斯的比例論。他沒有代數符號,沒有小數點,沒有電腦。他唯一的武器,是邏輯。

偵查過程#

線索一:曲線抓不到,就用直線包圍它#

阿基米德的第一個推理轉折是:既然圓周是彎的、摸不著的,那就派兩隊「線人」從內外兩側夾擊。

他在圓內接一個正多邊形,又在圓外切一個正多邊形。圓周長 \(C\) 必定被夾在兩者之間。設圓直徑為 \(d\) ,內接多邊形周長為 \(p_n\) ,外切多邊形周長為 \(P_n\) ,邊數為 \(n\) ,則:

\[ p_n < C < P_n \]

兩邊同除以 \(d\) ,就得到圓周率的上下界。邊數越多,包圍圈越緊。這就是窮竭法的靈魂:不直接追捕無限,而是用有限步步進逼,把誤差逼到牆角。

線索二:從六邊形出發,倍增邊數#

偵查從最好下手的地方開始:正六邊形。內接正六邊形邊長等於半徑,這是現成的突破口。

阿基米德用角平分線的幾何關係,把邊數一路倍增: \(6 \to 12 \to 24 \to 48 \to 96\) 。每倍增一次,就用勾股定理與比例關係更新邊長。設內接 \(n\) 邊形邊長為 \(s_n\) ,外切 \(n\) 邊形邊長為 \(t_n\) ,迭代關係可寫成現代形式:

\[ s_{2n} = \sqrt{2 - \sqrt{4 - s_n^2}} \]
\[ t_{2n} = \frac{2 s_{2n} t_n}{t_n + s_n} \]

他全程只用分數運算,硬是把開方的不精確性用不等式兩頭擋住。算到九十六邊形時,包圍圈終於收網。

線索三:收網數字#

經過冗長的分數圍捕,阿基米德宣布了數學史上第一份「數值誤差報告」:

\[ 3\frac{10}{71} < \pi < 3\frac{1}{7} \]

換成小數,約為 \(3.1408 < \pi < 3.1429\) 。嫌犯 \(\pi\) 的活動範圍,被壓縮到寬度不到 \(0.003\) 的小巷裡。

邊數 \(n\) 內接下界 外切上界 區間寬度
\(6\) \(3.0\) \(3.4641\) \(0.4641\)
\(12\) \(3.1058\) \(3.2154\) \(0.1096\)
\(24\) \(3.1326\) \(3.1597\) \(0.0271\)
\(48\) \(3.1394\) \(3.1461\) \(0.0067\)
\(96\) \(3.1408\) \(3.1429\) \(0.0021\)

上表為現代換算值,阿基米德原著全用分數表述,但夾擊結構完全一致。

parallel 案件:王冠裡的銀#

另一頭,王冠案也在收網。阿基米德在浴缸裡靈光一閃:物體浸入水中減輕的重量,等於它排開的水重。設物體重為 \(W\) ,浮力為 \(B\) ,排開液體密度為 \(\rho\) ,體積為 \(V\) ,則:

\[ B = \rho V g \]
\[ W_{水中} = W - B \]

金比銀重,只要把王冠與等重純金同浸水中,比較溢水量,摻假無所遁形。傳說他裸奔上街高喊「尤里卡」,正是這樁並案告破的瞬間。

理論定性:窮竭法的三條鐵律#

  1. 夾擊:構造可計算的上下界序列,而非猜一個近似值。
  2. 單調收斂:邊數增加時,下界遞增、上界遞減,絕不反覆。
  3. 誤差可證:每一步的誤差都有明確上界,這正是現代數值分析的祖訓。

結案報告#

阿基米德破了雙案。圓周率案中,他證明曲線量可以用直線序列任意逼近,開創了「數值逼近」的先河;王冠案中,他留下浮力定律,成為流體靜力學的起點。

此案的遺產極為深遠:

  • 方法遺產:窮竭法是積分的雛形,兩千年後被牛頓、萊布尼茲 formalize 為微積分,又被馮·諾伊曼搬進電腦。
  • 精神遺產:「給出近似值時必須同時給出誤差界」,這條規矩從本案一直沿用到今日的區間運算與驗證計算。
  • 未竟線索:阿基米德沒有代數符號與極限語言, \(96\) 邊形已是人力極限。真正的「無限細分」自動化,要等到尤拉方法與電腦登場。

用現代眼光看,本案是計算模擬學的第零章:世界可以寫成幾何,幾何可以化為算術,算術可以一步步逼近真相。

證據與工具#

  • 核心不等式:圓周率被鎖定在 \(3\frac{10}{71} < \pi < 3\frac{1}{7}\) 之間。
  • 夾擊公式:內接周長 \(p_n\) 與外切周長 \(P_n\) 構成的雙序列,是最早的收斂性證明框架。
  • 浮力公式: \(B = \rho V g\) ,靜水稱重法至今仍是密度量測標準。
  • 手算工具:角平分定理、勾股定理、歐多克索斯比例論;全程分數運算,以不等式控制開方誤差。
  • 可重演實驗:取圓直徑 \(d = 1\) ,用程式將邊數倍增至 \(n = 1536\) ,觀察上下界如何收斂到 \(3.1416\) 附近。
  • 延伸閱讀:《圓的度量》、《論浮體》;後續案件直通牛頓運動方程,因為微積分正是窮竭法的合法繼承人。

補充:程式實作#

對應程式:-0230-archimedes_pi.py

本文核心理論是雙側夾擊:圓周長必介於內接與外切正多邊形周長之間。 設圓半徑為 \(1\) ,內接正 \(n\) 邊形周長為 \(2n \sin(\pi/n)\) ,外切周長為 \(2n \tan(\pi/n)\) 。 兩者同除以直徑 \(2\) ,即得下界 \(L = n \sin(\pi/n)\) 與上界 \(U = n \tan(\pi/n)\) 。 邊數沿 \(6 \to 12 \to 24 \to 48 \to 96\) 倍增時,下界遞增、上界遞減,寬度單調收縮。 兇手 \(\pi\) 終於被逼進寬度僅 \(1.68e-03\) 的死巷,長達兩千年的圍捕在此收網。

執行指令:

python3 _code/-0230-archimedes_pi.py

實測關鍵輸出(本機真實執行結果抄錄):

n= 6: 下界 3.0000000000,上界 3.4641016151,寬度 4.64e-01
n=12: 下界 3.1058285412,上界 3.2153903092,寬度 1.10e-01
n=24: 下界 3.1326286133,上界 3.1596599421,寬度 2.70e-02
n=48: 下界 3.1393502030,上界 3.1460862151,寬度 6.74e-03
n=96: 下界 3.1410319509,上界 3.1427145996,寬度 1.68e-03
true pi = 3.1415926536,落在下界與上界之間
VERIFY lower96 = 3.141032(大於 $3.1408$ ,符合阿基米德下界)
VERIFY upper96 = 3.142715(小於 $3.1429$ ,符合阿基米德上界)
PASS

讀者可將邊數列延伸至 \(n = 1536\) ,觀察區間寬度縮小的速率,並對照真值 \(3.1415926536\) 計算誤差。

上述下界與上界均為程式以雙精度三角函數直接求值所得,未使用分數逼近,與原文分數表述互相印證。

完整程式如下:

# -*- coding: utf-8 -*-
"""0230 Archimedes 夾擊 pi(對應 wiki:計算模擬學 / 阿基米德窮竭法求 pi)
圓半徑=1:內接正 n 邊形周長=2n·sin(pi/n),外切正 n 邊形周長=2n·tan(pi/n),
故下界 L=n·sin(pi/n) < pi < n·tan(pi/n)=上界 U。
取 n=6,12,24,48,96(倍增),驗證 n=96 時 3.1408 < pi < 3.1429。
只用 numpy,固定種子。
"""
import numpy as np

np.random.seed(0)

ns = [6, 12, 24, 48, 96]
print(f"{'n':>5} {'lower=n sin(pi/n)':>20} {'upper=n tan(pi/n)':>20} {'width':>12}")
results = {}
for n in ns:
    lower = n * np.sin(np.pi / n)
    upper = n * np.tan(np.pi / n)
    results[n] = (lower, upper)
    print(f"{n:>5d} {lower:>20.10f} {upper:>20.10f} {upper - lower:>12.2e}")

lo96, hi96 = results[96]
print(f"true pi = {np.pi:.10f}")
print(f"96邊: 下界={lo96:.6f} (理論下界→pi), 上界={hi96:.6f}")

# 驗證數字:理論值 vs 實測
ok_lo = lo96 > 3.1408
ok_hi = hi96 < 3.1429
ok_bracket = (lo96 < np.pi < hi96)
print(f"VERIFY lower96={lo96:.6f} (>3.1408? {ok_lo})")
print(f"VERIFY upper96={hi96:.6f} (<3.1429? {ok_hi})")
print(f"VERIFY bracket: {lo96:.6f} < pi={np.pi:.6f} < {hi96:.6f} ? {ok_bracket}")
assert ok_lo, f"下界不夠緊: {lo96}"
assert ok_hi, f"上界不夠緊: {hi96}"
assert ok_bracket, "pi 不在夾擊區間內"
print("PASS")