4.3 自然數與歸納法#
本節目標#
- 理解 Peano 公理與 Lean 中
Nat的歸納型別定義 - 認識 recursor 與
inductiontactic 的關係 - 用數學歸納法證明 \(\sum_{i=1}^{n} i = \frac{n(n+1)}{2}\)
- 使用
omega與decide處理線性算術與可判定命題 - 對照 Lean 歸納與程式遞迴
Peano 公理#
Giuseppe Peano 於 1889 年形式化自然數:
- \(0\) 是自然數。
- 每個自然數 \(n\) 有後繼 \(\mathrm{succ}(n)\) 。
- \(0\) 不是任何數的後繼。
- \(\mathrm{succ}\) 是單射。
- 歸納原理:若 \(P(0)\) 且 \(P(n) \Rightarrow P(\mathrm{succ}(n))\) ,則對所有 \(n\) , \(P(n)\) 。
flowchart LR
Z["0"] --> S1["succ 0 (= 1)"] --> S2["succ 1 (= 2)"] --> S3["succ 2 (= 3)"] --> More["..."]
Ind["歸納原理:\n在 0 起步, 每步被 succ 保住\n⇒ 整條鏈都是 P"] -.-> ZNat 作為歸納型別#
在 Lean 中,Nat 幾乎逐字就是 Peano 公理:
-- Lean 中 Nat 的定義(簡化示意)
inductive Nat where
| zero : Nat
| succ (n : Nat) : Nat
inductive 宣告同時生成了:
- 兩個建構子:
Nat.zero、Nat.succ(公理 1、2) - 自動證明的單射性與不相交性(公理 3、4,如
Nat.succ.injEq、Nat.succ_ne_zero) - 一個 recursor:
Nat.rec(公理 5)
recursor:歸納原理的機械形式#
#check @Nat.rec
-- Nat.rec {motive : Nat → Sort u}
-- (zero : motive Nat.zero)
-- (succ : (n : Nat) → motive n → motive n.succ)
-- (t : Nat) : motive t
Nat.rec 說:要對任意 t : Nat 證明 motive t,只需提供
zero的情況:motive Nat.zerosucc的情況:給定motive n,能構造motive (n + 1)
這正是歸納法:基底 + 歸納步驟。induction tactic 只是 recursor 的語法糖。
歸納法證明: \(\sum_{i=1}^{n} i = \frac{n(n+1)}{2}\)#
先寫成可計算的函數(像寫遞迴程式):
import Mathlib.Tactic
def sumTo (n : ℕ) : ℕ :=
match n with
| 0 => 0
| n + 1 => sumTo n + (n + 1)
#eval sumTo 100 -- 5050
定理陳述(避開除法,用乘法形式: \(2\sum = n(n+1)\) ):
theorem sumTo_correct (n : ℕ) : sumTo n * 2 = n * (n + 1) := by
induction n with
| zero => rfl -- 基底:0 * 2 = 0 * 1,由定義成立
| succ k ih => -- 歸納步驟
rw [sumTo, ih] -- 展開定義,代入歸納假設
ring -- 多項式恆等式交給 ring
注意三個要素:
induction n with產生兩個子目標(zero、succ),Infoview 會分別顯示。ih(induction hypothesis)是自動引入的歸納假設:sumTo k * 2 = k * (k + 1)。ring是 mathlib 的交換環自動化 tactic,專門處理+、*的多項式恆等式。
歸納的推廣形式#
有時歸納假設需要加強。例如 \(\sum_{i=1}^{n} i > \frac{n^2}{2}\) 這類不等式,弱假設不夠用時,先證一個更強的陳述(稱為 strengthening the induction hypothesis):
-- 練習前示範:證明 sumTo n ≥ n,逐步歸納
theorem sumTo_ge (n : ℕ) : sumTo n ≥ n := by
induction n with
| zero => rfl
| succ k ih =>
rw [sumTo]
calc sumTo k + (k + 1) ≥ k + (k + 1) := Nat.add_le_add_right ih _
_ = k + k + 1 := Nat.add_assoc k k 1
_ ≥ k := by omega
omega:線性算術自動證明#
omega 專門處理線性整數 / 自然數算術(Presburger 算術的子集):
example (n : ℕ) : n + 3 = 3 + n := by omega
example (a b c : ℤ) (h1 : a + 2*b = c) (h2 : a = b) : 3*b = c := by
omega
example (n : ℕ) (h : n > 0) : n ≥ 1 := by omega
omega 能消化 context 中的線性假設,瞬間產出證明。經驗法則:凡線性不等式與等式,先丟給 omega;ring 負責非線性恆等式。
decide:可判定命題#
若命題的型別類有 Decidable 實例,decide 直接計算答案:
example : 2 ^ 10 = 1024 := by decide
example (n : ℕ) (h : n < 5) : sumTo n ≤ 10 := by
decide -- 失敗!n 是變數,無法枚舉 —— decide 只對封閉的具體命題有效
example : sumTo 4 ≤ 10 := by decide -- OK:sumTo 4 = 10 可計算
decide 的本質:把「證明」化為「執行程式」。這是 Curry–Howard 的純粹展現 —— 檢查證明就是跑演算法。
歸納與遞迴的對照#
Lean 的定義遞迴與證明歸納是同一枚硬幣的兩面:
| 程式世界 | 證明世界 |
|---|---|
def 的遞迴定義 | induction 的歸納結構 |
| 基底情況 return 值 | 基底情況的證明 |
| 遞迴呼叫自己 | 使用歸納假設 ih |
| 終止性檢查 | 歸納的良基性(well-foundedness) |
flowchart TB
subgraph 程式
F["sumTo (n+1)\n= sumTo n + (n+1)"] -->|"遞迴呼叫"| F0["sumTo 0 = 0"]
end
subgraph 證明
S1["succ 情況\n用 ih"] -->|"依賴"| S0["zero 情況\nrfl"]
end
F -.->|"同構"| S1
F0 -.->|"同構"| S0呼叫 sumTo (n+1) 展開成 sumTo n + (n+1),與 tactic rw [sumTo] 做的事完全一樣 —— rw 就是「函數展開」。
強歸納(strong induction)與其他形式#
有時需要「假設所有小於 n 的情況都成立」,即強歸納:
-- 強歸納由良基遞迴支援;mathlib 提供 induction … using 及相關引理
-- 例如:每個 n ≥ 1 都有前驅(示範結構)
theorem exists_pred (n : ℕ) (h : n > 0) : ∃ m, n = m + 1 := by
cases n with
| zero => omega
| succ k => exact ⟨k, rfl⟩
cases 是歸納的退化形式:只拆建構子、不引入 ih。遇到「要對前一個情況做假設」的問題,先想清楚 ih 應該長什麼樣,再決定 induction 的陳述方式。
小結與練習#
Nat是歸納型別,Peano 公理由inductive宣告自動獲得。Nat.rec是歸納原理的機械形式,inductiontactic 是其語法糖。omega管線性算術、ring管多項式恆等式、decide管可計算的封閉命題。
練習:
- 定義
def fact : ℕ → ℕ(0 → 1,n+1 → fact n * (n+1)),並證明fact 5 = 120(用decide或rfl)。 - 證明:
sumTo (n + 1) = sumTo n + n + 1(其實就是定義展開)。 - 用歸納法證明 \(\sum_{i=1}^{n} i^2 = \frac{n(n+1)(2n+1)}{6}\) (提示:定義
sumSq,用乘法形式 \(6\sum = n(n+1)(2n+1)\) ,靠ring收尾)。 - 證明:對所有
n : ℕ,n < n + 1(可用Nat.lt_succ_self或 omega)。 - 解釋為什麼
example (n : ℕ) : n + n = 2 * n := by decide會失敗,但by omega會成功。