12.3 用 SymPy 驗證極限與求和的交換#
學習動機#
前兩節把「測度」與「函數空間」建立起來了。整本書走到這裡,所有的技術準備都已就位,只剩下一個貫穿全書的核心問題:
把無限個運算(求和、積分、取極限)重新排列,什麼時候還合法?
這個問題在第 5 章以「 \(x^n\) 的極限與積分」的形式出現過一次,在第 6 章以「Cesàro 和」與「Riemann 重排定理」的形式出現過兩次,在第 11 章以「Fourier 級數能否逐項積分」的形式出現過一次。每一次我們都靠「額外假設」把問題壓下去:非負性、絕對收斂、一致收斂、單調性。現在是時候把這些「零散的事例」統一成一張地圖。
本節的策略是反過來的:不從定理出發,而是用 SymPy 讓計算機替我們做實驗。
- 先算出 \(\lim_{n\to\infty}\sum_{k=1}^{n}\frac{1}{k^2} = \frac{\pi^2}{6}\) 這個 Basel 等式(Euler 1735 年的名篇);
- 用
sp.Sum把「無窮級數」變成「部分和的極限」,示範 SymPy 如何區分收斂與絕對收斂; - 示範 \(n \to \infty\) 的 Riemann 和 → 積分,也就是
sp.limit與sp.integrate的互換; - 示範 Fatou 與控制收斂定理的可驗證實例(用符號積分 + 極限);
- 最後是重點:示範沒有控制時交換會失敗的具體反例,並把 SymPy 變成一個「反例搜尋器」。
flowchart TD
A["無限運算的重新排列"] --> B["lim_n sum_k int f_k<br/>vs int sum_k f_k"]
A --> C["lim_n int f_n<br/>vs int lim_n f_n"]
A --> D["lim_n sum_k f_k(x)<br/>vs sum_k f_k(x)"]
B --> E["合法:絕對可積 / 非負<br/>Fubini / Tonelli"]
B --> F["不合法:僅條件收斂"]
C --> G["合法:Fatou 不等式<br/>等號需單調性或控制"]
C --> H["不合法:移動尖峰"]
D --> I["合法:一致收斂"]
D --> J["不合法:僅點態收斂"]
E --> K["SymPy 可驗證具體實例"]
F --> K
H --> L["SymPy 當反例搜尋器"]
J --> L12.3.1 兩種寫法,三種交換#
「極限與求和可交換」其實有三個不同的版本。把它們寫清楚,後面的實驗才有座標:
(一)極限與積分(最基本的形式)
(二)極限與求和(級數的版本)
這在第 6 章已經看過:一致收斂是充分條件;單調收斂是另一個;只有點態收斂則失敗。
(三)求和與積分(最有趣的形式)
這正是 Fubini 定理(絕對可積時)與 Tonelli 定理(非負時)處理的東西。第 12.1 節我們已經看過:若 \(\int\int |f| = \infty\) 而 \(f\) 有正有負,兩個迭代積分甚至可以不相等。
三者之間的關係是:(三)是(一)的推廣(把 \(f_n\) 換成部分和 \(\sum_{k\le n} f_k\) ),而 (一)與(二)之間沒有直接蘊含——這正是 Fatou 引理存在的理由:它給出的是一個不等式,而不是等式。
現在讓我們開始做實驗。
12.3.2 Basel 等式: \(\sum_{k=1}^{\infty}\frac{1}{k^2} = \frac{\pi^2}{6}\)#
Euler 在 1735 年利用正弦函數的無窮乘積
比較 \(x^2\) 係數,得到 \(\sum_{n\ge 1} \frac{1}{n^2} = \frac{\pi^2}{6}\) 。這個等式的重要性不在 \(\pi^2\) 而在 \(\pi\) :它把兩個原本毫無關聯的對象(倒數平方和、圓周率)連結起來,而且它是「把極限與求和交換」的第一個輝煌案例。
SymPy 用超幾何級數求和(Gosper 演算法)直接給出答案。我們順便把「部分和」與「積分判別法的誤差」一起算出來。
import sympy as sp
k = sp.symbols('k', integer=True, positive=True)
N = sp.symbols('N', integer=True, positive=True)
x = sp.symbols('x', real=True)
print('sp.summation(1/k^2, k=1..oo) =', sp.summation(1 / k**2, (k, 1, sp.oo)))
S = sp.summation(1 / k**2, (k, 1, N))
print('部分和 S_N =', S)
print('sp.integrate(1/x^2, x=1..oo) =', sp.integrate(1 / x**2, (x, 1, sp.oo)))
for M in [10, 100, 1000]:
val = sp.summation(1 / k**2, (k, 1, M))
print(f' N={M:5d}: S_N = {sp.N(val, 12)} 誤差 pi^2/6 - S_N = {sp.N(sp.pi**2/6 - val, 12)}')
print('積分判別法給出的誤差上界 1/N,其極限 =', sp.limit(1 / N, N, sp.oo))
預期輸出:
sp.summation(1/k^2, k=1..oo) = pi**2/6
部分和 S_N = harmonic(N, 2)
sp.integrate(1/x^2, x=1..oo) = 1
N= 10: S_N = 1.54976773117 誤差 pi^2/6 - S_N = 0.0951663356817
N= 100: S_N = 1.63498390018 誤差 pi^2/6 - S_N = 0.00995016666333
N= 1000: S_N = 1.64393456668 誤差 pi^2/6 - S_N = 0.000999500166667
積分判別法給出的誤差上界 1/N,其極限 = 0
這段程式碼教會我們什麼?
sp.summation給出的是精確值pi**2/6,而sp.summation(1/k**2, (k, 1, N))給出的是符號的部分和harmonic(N, 2)——SymPy 用專門的廣義調和數符號來表示它。兩者之間的關係正是第 6 章的定義: \(\sum_{k\ge1} \frac{1}{k^2} = \lim_{N\to\infty} S_N\) 。而誤差 \(\approx \frac{1}{N}\) 的衰減率(來自積分判別法)正是「收斂的速率」——這是比「收斂」更有用的資訊。
12.3.3 sp.Sum:把「無窮」變成「極限」#
SymPy 有兩個求和相關的物件:
sp.summation(f, (k, a, b)):立刻嘗試把和求出來,必要時回傳未求值的物件;sp.Sum(f, (k, a, b)):保留求和的形式(未求值),可以呼叫.doit()稍後求值,並且攜帶收斂性資訊。
sp.Sum 最有價值的兩個方法是 is_convergent() 與 is_absolutely_convergent()——它們讓 SymPy 像一個「收斂性裁判」。
import sympy as sp
k = sp.symbols('k', integer=True, positive=True)
N = sp.symbols('N', integer=True, positive=True)
x = sp.symbols('x', real=True)
# ---- sp.Sum:把「未求值」和「已求值」分開 ----
S = sp.Sum(1 / k**2, (k, 1, sp.oo))
print('未求值的 Sum 物件 =', S)
print('S.is_convergent() =', S.is_convergent())
print('S.is_absolutely_convergent() =', S.is_absolutely_convergent())
print('S.doit() =', S.doit())
print('sp.summation(...) 直接求值 =', sp.summation(1 / k**2, (k, 1, sp.oo)))
# ---- 「極限 = 部分和的極限」:把無窮級數換成有限和的極限 ----
print()
partial = sp.summation(1 / k**2, (k, 1, N))
print('部分和 S_N =', partial)
print('sp.limit(S_N, N, oo) =', sp.limit(partial, N, sp.oo))
# ---- 條件收斂級數:sp.Sum 會告訴我們它不是絕對收斂 ----
print()
T = sp.Sum((-1)**(k + 1) / k, (k, 1, sp.oo))
print('T =', T)
print('T.is_convergent() =', T.is_convergent())
print('T.is_absolutely_convergent() =', T.is_absolutely_convergent())
print('T.doit() =', T.doit())
# ---- 幾何級數:收斂條件被寫進答案裡 ----
print()
print('sp.summation(x**k, k=1..oo) =', sp.summation(x**k, (k, 1, sp.oo)))
print('sp.summation(1/k, k=1..oo) =', sp.summation(1 / k, (k, 1, sp.oo)))
預期輸出:
未求值的 Sum 物件 = Sum(k**(-2), (k, 1, oo))
S.is_convergent() = True
S.is_absolutely_convergent() = True
S.doit() = pi**2/6
sp.summation(...) 直接求值 = pi**2/6
部分和 S_N = harmonic(N, 2)
sp.limit(S_N, N, oo) = pi**2/6
T = Sum((-1)**(k + 1)/k, (k, 1, oo))
T.is_convergent() = True
T.is_absolutely_convergent() = False
T.doit() = log(2)
sp.summation(x**k, k=1..oo) = Piecewise((x/(1 - x), Abs(x) < 1), (Sum(x**k, (k, 1, oo)), True))
sp.summation(1/k, k=1..oo) = oo
三個關鍵觀察:
sp.Sum是「可攜帶證據的物件」。T.is_convergent() = True但T.is_absolutely_convergent() = False——SymPy 直接告訴我們這是條件收斂級數(第 6 章的主題),而且和的仍是 \(\log 2\) 。- 收斂條件會寫進答案裡。
sp.summation(x**k, ...)的答案是Piecewise:|x| < 1時給出 \(\frac{x}{1-x}\) ,其他情況保留未求值的Sum。這正是第 6.1 節強調的重點——收斂條件是答案的一部分。 - 「部分和 + 極限」是把無窮變成有限的標準套路:
sp.summation(1/k**2, (k, 1, N))給出harmonic(N, 2),再sp.limit(..., N, oo)就回到pi**2/6。這正是柯西收斂定義(第 6.1 節)在程式裡的樣子。
12.3.4 Riemann 和 → 積分:sp.limit 與 sp.integrate 的互換#
「極限與求和可交換」的一個重要特例是:**把 \([0,1]\) 切成 \(n\) 份,取 Riemann 和,再令 \(n \to \infty\) **:
這就是黎曼積分的「和」與「極限」的互換。我們用 SymPy 把它變成一個可以自動化的驗證流程:只要給定 \(f\) 的符號表達式,sp.summation + sp.limit 就會自動把極限算出來,再跟 sp.integrate 的結果對照。
import sympy as sp
n = sp.symbols('n', integer=True, positive=True)
k = sp.symbols('k', integer=True, positive=True)
x = sp.symbols('x', real=True)
# ---- Riemann 和 -> 積分 ----
R = sp.summation((k / n)**2 / n, (k, 1, n))
print('Riemann 和 sum_{k=1}^n (k/n)^2 (1/n) =', R)
print('其極限 =', sp.limit(R, n, sp.oo))
print('對應的積分 =', sp.integrate(x**2, (x, 0, 1)))
# ---- 一般化的 Riemann 和:f(x) = exp(x) ----
R2 = sp.summation(sp.exp(k / n) / n, (k, 1, n))
print()
print('exp 的 Riemann 和 =', R2)
print('其極限 =', sp.limit(R2, n, sp.oo))
print('對應的積分 =', sp.integrate(sp.exp(x), (x, 0, 1)))
# ---- 左端點版本:sum_{k=0}^{n-1} f(k/n) (1/n) ----
R3 = sp.summation((k / n)**3 / n, (k, 0, n - 1))
print()
print('左端點 Riemann 和 =', sp.limit(R3, n, sp.oo))
print('對應的積分 =', sp.integrate(x**3, (x, 0, 1)))
# ---- 數值核對:不同 n 的 Riemann 和與積分的差距 ----
print()
for m in [10, 100, 1000]:
rs = sum(sp.Rational(k, m)**2 / m for k in range(1, m + 1))
print(f' n={m:5d}: Riemann 和 = {sp.N(rs, 12)} 誤差 = {sp.N(sp.Rational(1, 3) - rs, 12)}')
預期輸出:
Riemann 和 sum_{k=1}^n (k/n)^2 (1/n) = (n**3/3 + n**2/2 + n/6)/n**3
其極限 = 1/3
對應的積分 = 1/3
exp 的 Riemann 和 = (exp(1/n) - exp((n + 1)/n))/(n*(1 - exp(1/n)))
其極限 = -1 + E
對應的積分 = -1 + E
左端點 Riemann 和 = 1/4
對應的積分 = 1/4
n= 10: Riemann 和 = 0.385000000000 誤差 = -0.0516666666667
n= 100: Riemann 和 = 0.338350000000 誤差 = -0.00501666666667
n= 1000: Riemann 和 = 0.333833500000 誤差 = -0.000500166666667
注意誤差的符號:右端點 Riemann 和低估了 \(\int_0^1 x^2\,dx = \frac13\) (因為 \(x^2\) 是凸函數,割線在曲線下方),誤差量級是 \(O(1/n)\) ——從 \(0.0517\) 到 \(0.00502\) 到 \(0.000500\) ,每個十倍 \(n\) 就把誤差除以約 \(10\) 。這與第 4.3 節用「梯形面積法」逼近積分時的誤差分析一致。
12.3.5 Fatou 與控制收斂的可驗證實例#
回到第 12.1 節的三個定理。現在有了 SymPy,我們可以把它們逐條核對:把 \(f_n\) 的符號表達式寫進去,讓 sp.integrate 算出每一個 \(\int f_n\) ,讓 sp.limit 算出它們的極限,再跟極限函數的積分對照。
import sympy as sp
x = sp.symbols('x', real=True)
n = sp.symbols('n', integer=True, positive=True)
# ---- 控制收斂定理:f_n = x^n (1-x),被 g = x(1-x) 控制 ----
g = x * (1 - x)
print('控制函數 g = x(1-x),int g =', sp.integrate(g, (x, 0, 1)), '(可積)')
print('|f_n| <= g : x^n <= x 對 x in [0,1] 成立')
J = sp.simplify(sp.integrate(x**n * (1 - x), (x, 0, 1)))
print('int f_n =', J, ' -> lim =', sp.limit(J, n, sp.oo))
print('lim f_n = 0 (a.e.),int lim f_n = 0 -> 等號成立 ✅')
# ---- 單調收斂定理(遞增):f_n = min(x, n) ----
print()
K = sp.integrate(sp.Min(x, n), (x, 0, 1))
print('int min(x,n) =', K, ' -> lim =', sp.limit(K, n, sp.oo))
print('lim f_n = x,int lim f_n =', sp.integrate(x, (x, 0, 1)), ' -> 等號成立 ✅')
# ---- Fatou 不等式:移動尖峰 f_n = n * 1_{(0,1/n)} ----
print()
I_move = sp.integrate(n, (x, 0, 1 / n))
print('int f_n =', I_move, ' -> liminf =', sp.limit(I_move, n, sp.oo))
print('liminf f_n = 0 (a.e.),int liminf f_n = 0 <= 1 -> Fatou 的不等式成立 ✅')
print('(注意這裡是不等式,不是等號:Fatou 從不保證等號成立)')
預期輸出:
控制函數 g = x(1-x),int g = 1/6 (可積)
|f_n| <= g : x^n <= x 對 x in [0,1] 成立
int f_n = 1/(n**2 + 3*n + 2) -> lim = 0
lim f_n = 0 (a.e.),int lim f_n = 0 -> 等號成立 ✅
int min(x,n) = 1/2 -> lim = 1/2
lim f_n = x,int lim f_n = 1/2 -> 等號成立 ✅
int f_n = 1 -> liminf = 1
liminf f_n = 0 (a.e.),int liminf f_n = 0 <= 1 -> Fatou 的不等式成立 ✅
(注意這裡是不等式,不是等號:Fatou 從不保證等號成立)
三個例子的結構對比值得記下:
| 例子 | 條件 | 結論 | SymPy 驗證的重點 |
|---|---|---|---|
| \(f_n = x^n(1-x)\) | 有可積控制 \(g = x(1-x)\) | 等號成立 | \(\int f_n \to 0 = \int \lim f_n\) |
| \(f_n = \min(x,n)\) | 單調遞增 | 等號成立 | \(\int f_n \uparrow \frac12 = \int x\) |
| \(f_n = n\,\mathbf{1}_{(0,1/n)}\) | 只有非負 | 只有不等式 | \(0 < 1\) ,Fatou 嚴格成立 |
12.3.6 沒有控制時,交換會失敗:三個具體反例#
現在來做反面實驗。每一個反例都對應一個「若不補上假設,結論就會壞掉」的教訓。
import sympy as sp
x = sp.symbols('x', real=True)
n = sp.symbols('n', integer=True, positive=True)
# ---- 反例 A:移動尖峰,極限與積分無法交換 ----
print('【反例 A】f_n(x) = n * 1_{(0,1/n)}(x) on [0,1]')
I_move = sp.integrate(n, (x, 0, 1 / n))
print(' lim_n int f_n =', sp.limit(I_move, n, sp.oo))
print(' lim_n f_n = 0 (a.e.),所以 int lim_n f_n = 0')
print(' 0 != 1 -> 沒有控制函數時,極限與積分的交換失敗 ❌')
# ---- 反例 B:黎曼和的極限交換在狄利克雷函數上失敗 ----
print()
print('【反例 B】f = 1_Q on [0,1],黎曼和隨採樣點而不同')
def dirichlet(xv):
"""xv 為 sympy 實數:有理回傳 1,無理回傳 0"""
return sp.Integer(1) if xv.is_rational else sp.Integer(0)
def riemann_sum(f, a, b, N, sampler):
h = (b - a) / N
return sp.simplify(sum(f(sampler(a + k * h)) for k in range(N)) * h)
for N in [4, 8, 16]:
r = riemann_sum(dirichlet, sp.Rational(0), sp.Rational(1), N, lambda t: t)
i = riemann_sum(dirichlet, sp.Rational(0), sp.Rational(1), N, lambda t: t + sp.sqrt(2) / 7)
print(f' N={N:3d}: 有理採樣的和 = {r} 無理採樣的和 = {i} -> 黎曼和不收斂 ❌')
print(' 而 Lebesgue 積分 = 1*m(Q∩[0,1]) + 0*m(其餘) = 0')
print(' -> 「黎曼和的極限 = 積分」這一步在這裡失效')
# ---- 對照組:a.e. 收斂時交換其實成立 ----
print()
print('【對照 C】f_n(x) = x^n on [0,1]')
J = sp.integrate(x**n, (x, 0, 1))
print(' lim_n int x^n dx =', sp.limit(J, n, sp.oo))
print(' 點態極限在 x=1 為 1、其餘為 0;因 {1} 的測度為 0,int lim = 0')
print(' -> 兩者相等,交換成立 ✅(真正的反例是尖峰那一組)')
預期輸出:
【反例 A】f_n(x) = n * 1_{(0,1/n)}(x) on [0,1]
lim_n int f_n = 1
lim_n f_n = 0 (a.e.),所以 int lim_n f_n = 0
0 != 1 -> 沒有控制函數時,極限與積分的交換失敗 ❌
【反例 B】f = 1_Q on [0,1],黎曼和隨採樣點而不同
N= 4: 有理採樣的和 = 1 無理採樣的和 = 0 -> 黎曼和不收斂 ❌
N= 8: 有理採樣的和 = 1 無理採樣的和 = 0 -> 黎曼和不收斂 ❌
N= 16: 有理採樣的和 = 1 無理採樣的和 = 0 -> 黎曼和不收斂 ❌
而 Lebesgue 積分 = 1*m(Q∩[0,1]) + 0*m(其餘) = 0
-> 「黎曼和的極限 = 積分」這一步在這裡失效
【對照 C】f_n(x) = x^n on [0,1]
lim_n int x^n dx = 0
點態極限在 x=1 為 1、其餘為 0;因 {1} 的測度為 0,int lim = 0
-> 兩者相等,交換成立 ✅(真正的反例是尖峰那一組)
三個反例的病根各不相同,這正是重點:
- 反例 A(移動尖峰):函數的「質量」不集中在固定的地方,而是跑到無窮遠又回來。整個區間上 \(\int f_n = 1\) 永遠不變,但極限函數卻是零。這說明 Fatou 的不等式 \(\int \liminf f_n \le \liminf \int f_n\) 中的「 \(\le\) 」永遠可能嚴格。
- 反例 B(狄利克雷函數):極限函數幾乎到處為零,但它「到處都是壞點」。這說明黎曼積分要求的「連續」條件無法用「測度為零的例外」來取代——除非你換成勒貝格積分(第 12.1 節)。
- 對照 C( \(x^n\) ):這組函數幾乎到處收斂到 \(0\) ,而 \(\{1\}\) 的測度為零,所以 Lebesgue 意義下的交換成立。這說明「 \(x^n\) 是反例」這句話只在黎曼積分的語境裡成立;換到 Lebesgue 積分,它就不再是反例。反例的成立與否,取決於你用哪一種積分——這是本節最值得咀嚼的一句話。
12.3.7 SymPy 不能證明 Lebesgue 定理,但能當「反例搜尋器」#
我們必須誠實地承認一件事:
SymPy 無法(也不打算)自動證明 Fatou 引理、單調收斂定理、控制收斂定理或 Fubini 定理。
原因是結構性的。這些定理的陳述裡含有:
- 「對幾乎所有 \(x\) 」——這是一個關於測度的量詞,而 SymPy 沒有測度論的模型(它的
integrate是黎曼型的); - 「對每個可測集 \(A\) 」——Fubini 定理的量詞範圍遠大於 SymPy 能處理的對象;
- 「存在一個可積函數 \(g\) 使 \(|f_n| \le g\) 」——這是一個存在性斷言,符號系統無法從具體的 \(f_n\) 自動構造出 \(g\) 。
但 SymPy 仍然非常有用,因為它可以扮演三種角色:
| 角色 | 做法 | 本節的例子 |
|---|---|---|
| 計算員 | 精確算出 \(\int f_n\) 與 \(\lim \int f_n\) | 12.3.5 的三個定理實例 |
| 裁判 | 用 is_convergent() 判斷收斂性 | 12.3.3 的 sp.Sum |
| 反例搜尋器 | 掃描參數族,找出讓定理失效的參數 | 下面這段程式 |
第三種角色最有價值:我們不只知道某個反例存在,而是能系統地找出反例的「門檻」。
import sympy as sp
x = sp.symbols('x', real=True)
n = sp.symbols('n', integer=True, positive=True)
b = sp.symbols('b', real=True)
# ---- 反例搜尋器 1:族 f_n(x) = x^(a*n) on [0,1] ----
print('【搜尋器 1】f_n(x) = x^(a*n),求 a 使「極限與積分」的交換失敗')
print(' a lim int f_n int lim f_n 交換失敗?')
for av in [0, sp.Rational(1, 2), 1, 2, 5]:
I = sp.integrate(x**(av * n), (x, 0, 1))
lim_I = sp.simplify(sp.limit(I, n, sp.oo))
lim_f = sp.Integer(1) # 對 x>0 極限為 1;零測度點 x=0 無關緊要
bad = sp.simplify(lim_I - lim_f) != 0
print(f' {str(av):>4s} {str(lim_I):>10s} {str(lim_f):>8s} {bad}')
# ---- 反例搜尋器 2:族 f_n(x) = n^b * 1_{(0,1/n)}(x) ----
print()
print('【搜尋器 2】f_n(x) = n^b * 1_{(0,1/n)}(x),求 b 使交換失敗')
print(' b lim int f_n int lim f_n 交換失敗?')
for bv in [0, sp.Rational(1, 2), 1, 2]:
I = sp.integrate(n**bv, (x, 0, 1 / n))
lim_I = sp.limit(I, n, sp.oo)
lim_f = sp.Integer(0)
bad = sp.simplify(lim_I - lim_f) != 0
print(f' {str(bv):>4s} {str(lim_I):>10s} {str(lim_f):>8s} {bad}')
# ---- 用符號把門檻寫出來(SymPy 無法直接對參數求極限,需分段)----
print()
lim_pw = sp.Piecewise((sp.Integer(0), b < 1), (sp.Integer(1), sp.Eq(b, 1)), (sp.oo, True))
print('lim_n n^(b-1) =', lim_pw)
print('=> 交換失敗的精確條件是 b >= 1(門檻由分段函數給出)')
預期輸出:
【搜尋器 1】f_n(x) = x^(a*n),求 a 使「極限與積分」的交換失敗
a lim int f_n int lim f_n 交換失敗?
0 1 1 False
1/2 0 1 True
1 0 1 True
2 0 1 True
5 0 1 True
【搜尋器 2】f_n(x) = n^b * 1_{(0,1/n)}(x),求 b 使交換失敗
b lim int f_n int lim f_n 交換失敗?
0 0 0 False
1/2 0 0 False
1 1 0 True
2 oo 0 True
lim_n n^(b-1) = Piecewise((0, b < 1), (1, Eq(b, 1)), (oo, True))
=> 交換失敗的精確條件是 b >= 1(門檻由分段函數給出)
搜尋器 1 的解讀: \(a = 0\) 時 \(f_n \equiv 1\) ,交換當然成立;任何 \(a > 0\) 都失敗。這是因為 \(x^{an}\) 在 \((0,1]\) 上恆等於「漸近於 \(1\) 」,所以 \(\int \lim f_n = 1\) ,而質量卻全部「滑」到 \(x = 0\) 附近去了。
搜尋器 2 的解讀:尖峰高度 \(n^b\) 與寬度 \(1/n\) 的乘積是 \(n^{b-1}\) ——這就是「質量」。 \(b < 1\) 時質量趨於零(交換成立), \(b = 1\) 時質量守恆為 \(1\) (失敗), \(b > 1\) 時質量發散(更失敗)。這就是 Fatou 不等式不能取等號的精確原因。
12.3.8 小結#
| 交換 | 合法條件 | 定理 | SymPy 驗證方式 |
|---|---|---|---|
| \(\lim \int f_n = \int \lim f_n\) | 有可積控制函數 \(g\) | 控制收斂定理 | sp.integrate + sp.limit |
| \(\lim \int f_n = \int \lim f_n\) | \(f_n\) 單調 | 單調收斂定理 | 同上(sp.Min 等) |
| \(\int \liminf f_n \le \liminf \int f_n\) | \(f_n \ge 0\) | Fatou 引理 | 同上 |
| \(\int \sum_k f_k = \sum_k \int f_k\) | 非負 | Tonelli 定理 | 巢狀 sp.integrate |
| \(\int \sum_k f_k = \sum_k \int f_k\) | 絕對可積 | Fubini 定理 | 巢狀 sp.integrate |
| \(\lim_n \sum_{k\le n} f_k(x) = \sum_k f_k(x)\) | 一致收斂 | 一致收斂定理 | 數值實驗 |
| \(\lim_n \sum_{k=1}^{n} f(k/n)\frac1n = \int f\) | \(f\) 連續 | 黎曼和極限定理 | sp.summation + sp.limit |
| \(\sum_{k\ge1}\frac{1}{k^2} = \frac{\pi^2}{6}\) | —— | Basel 等式(Euler 1735) | sp.summation |
給讀者的三條實務建議:
- 永遠先確認收斂性:
sp.Sum(...).is_convergent()與is_absolutely_convergent()是最便宜的保險。 - 把「無窮」寫成「極限」:
sp.summation(f, (k, 1, N))+sp.limit(..., N, oo)能把大多數「無窮級數」的問題變成 SymPy 可以處理的極限計算。 - 不要相信回傳
Piecewise的答案:那表示 SymPy 知道答案但不知道適用條件,此時收斂條件必須由你自己讀出來。
下一章是第 13 章:我們將把這整本書的技術(Euler–Lagrange 方程、變分法、梯度下降)放進優化的框架裡,看看數學家如何在沒有導數的情況下尋找最佳解。
習題#
習題 12.3.1(Euler 的無窮乘積與 Basel 等式) (a) 用 sp.product 驗證正弦函數的無窮乘積: \(\prod_{n=1}^{\infty}\left(1 - \frac{x^2}{n^2}\right) = \frac{\sin(\pi x)}{\pi x}\) (先用 sp.product 的有限形式 sp.product((1 - x**2/n**2), (n, 1, N)) 再取極限)。(b) 在 \(x = \frac{1}{2}\) 處求值,得到 \(\prod_{n\ge1}\left(1 - \frac{1}{4n^2}\right) = \frac{2}{\pi}\) 。(c) 用 sp.series 比較兩邊的 \(x^2\) 係數,重建 Euler 推出 \(\sum \frac{1}{n^2} = \frac{\pi^2}{6}\) 的過程。
習題 12.3.2(收斂性裁判的用法) 用 sp.Sum 的 is_convergent() 與 is_absolutely_convergent() 檢查下列級數,並用它們各自的 .doit() 結果驗證你的判斷: \(\sum \frac{1}{k^2}\) 、 \(\sum \frac{(-1)^{k+1}}{k}\) 、 \(\sum \frac{1}{k}\) 、 \(\sum \frac{1}{\sqrt{k}}\) 、 \(\sum \frac{k}{k^2}\) 。整理成一張表,並說明「條件收斂」在第 6 章的 Riemann 重排定理裡扮演的角色。
習題 12.3.3(Riemann 和的誤差率) (a) 對 \(f(x) = x^3\) 與 \(f(x) = \sin(\pi x)\) ,用 sp.summation + sp.limit 求出右端點 Riemann 和的極限,並與 sp.integrate 對照。(b) 對 \(f(x) = |x - \frac{1}{2}|\) 重做一次——注意這裡極限依然存在,但等於 \(\frac14\) ;說明為什麼。(c) 最後用狄利克雷函數說明:什麼樣的函數會讓 Riemann 和的極限不存在。
習題 12.3.4(Fatou 與控制收斂的邊界) (a) 構造一個序列 \(f_n \ge 0\) 使得 \(\int \liminf f_n = 0\) 但 \(\liminf \int f_n = 1\) (移動尖峰)。(b) 構造一個序列 \(g_n\) 幾乎處處收斂到 \(g\) ,且存在可積的控制函數使得 \(\int g_n \to \int g\) ,並證明這個可積控制函數不可能是前者的那個「尖峰控制」。(c) 由此說明:控制收斂定理中的「存在可積 \(g\) 」這個假設無法被削弱成「 \(\int |f_n|\) 有界」。
習題 12.3.5(反例搜尋器的擴充) 把搜尋器 1 的 \(f_n(x) = x^{an}\) 換成高斯尖峰 \(f_n(x) = e^{-|x - \frac{1}{2}|^{2}/n}\) 。(a) 用 sp.integrate 計算 \(\int_0^1 f_n\, dx\) 與其極限,說明峰值高度如何讓「質量」維持為常數。(b) 計算 \(\int \lim_n f_n\, dx\) 並說明 Fatou 為什麼是嚴格不等式。(c) 再把尖峰改成 \(f_n(x) = n\,e^{-|x - \frac{1}{2}|^{2}/n}\) ,觀察「寬度 × 高度」的量級分析如何預測交換是否失敗。