4.4 集合、函數與實數#
本節目標#
- 理解 mathlib 的
Set α = α → Prop:集合即述詞 - 用
funext證明函數相等,進而證明集合相等 - 掌握
Injective、Surjective、Bijective的形式化定義與證明 - 概觀實數的 Cauchy 序列完備化建構
- 使用 lattice 的
le_antisymm與nlinarith - 完整走過一個小定理的開發流程:從
sorry到完整證明
集合即述詞#
在 ZFC 中集合是原始物件;在 Lean 的型別論裡,集合是一個函數:
import Mathlib.Tactic
-- Set α 的定義(mathlib 原始碼,一字不差)
-- def Set (α : Type*) := α → Prop
variable (α : Type) (s : Set α)
#check s -- s : Set α
#check s : α → Prop -- 同一個東西的另一種看法
-- x ∈ s 就是「把述詞 s 套用在 x 上」
-- def Set.instMembership : Membership α (Set α) := ⟨fun s a => s a⟩
-- 交集、聯集、補集直接由邏輯連接詞定義
example (a b : Set α) : Set α := a ∩ b -- {x | x ∈ a ∧ x ∈ b}
example (a b : Set α) : Set α := a ∪ b -- {x | x ∈ a ∨ x ∈ b}
example (a : Set α) : Set α := aᶜ -- {x | x ∉ a}
集合論的許多公理(如外延公理)在此不證自明,因為它們是函數與邏輯的性質:
-- 集合相等 = 逐點相等 = 函數相等(外延性的型別論化身)
example (a b : Set α) : a = b ↔ ∀ x, x ∈ a ↔ x ∈ b := by
simp [Set.ext_iff]
funext:函數相等#
兩個函數相等,當且僅當在每個輸入上輸出相等:
#check funext
-- funext : (∀ x, f x = g x) → f = g
#check funext_iff
-- funext_iff : f = g ↔ ∀ x, f x = g x
-- 例:證明兩個集合(述詞)相等
example (a : Set α) : a ∩ aᶜ = ∅ := by
funext x -- 把集合相等化為逐點命題 x ∈ a ∩ aᶜ ↔ x ∈ ∅
constructor
· rintro ⟨h1, h2⟩ -- h1 : x ∈ a, h2 : x ∉ a
exact h2 h1 -- 直接矛盾
· intro h
exact absurd h (Set.notMem_empty x)
funext x 在 tactic 模式中:目標若是 a = b(兩者為函數),就逐點化。這是函數外延性(function extensionality),它並非 Lean 核心的判定相等,而是一條需要引用的引理。
單射、滿射、雙射#
#check @Function.Injective
-- Injective f : ∀ a b, f a = f b → a = b
#check @Function.Surjective
-- Surjective f : ∀ b, ∃ a, f a = b
#check @Function.Bijective
-- Bijective f : Injective f ∧ Surjective f
範例:證明 \(f(n) = 2n + 1\) 是單射,且不是滿射。
def f (n : ℕ) : ℕ := 2 * n + 1
example : Function.Injective f := by
intro a b hab
simp only [f] at hab
omega -- 線性算術:由 2a+1 = 2b+1 推出 a = b
example : ¬ Function.Surjective f := by
intro hsurj
obtain ⟨k, hk⟩ := hsurj 0 -- 0 必須有原像
simp [f] at hk -- 2k + 1 = 0,在 ℕ 中不可能
omega
經典定理 — Cantor–Bernstein 也內建於 mathlib:
#check @Function.Bijective_iff_hasInverse
實數的建構概觀#
Lean 的 ℝ 採用與 Dedekind 不同的路線:Cauchy 序列完備化(由 mathlib 的 Mathlib/Data/Real/Basic 完成):
flowchart LR
N["ℕ\n歸納型別"] --> Z["ℤ\n差對 (a,b) 商掉 a+b'=a'+b"] --> Q["ℚ\n整數對 (a,b), b≠0"]
Q --> CS["Cauchy 序列\nℚ → ℚ(保持有理數運算)"]
CS -->|"|商掉「差趨近 0」的等價類|" R["ℝ\n完備阿基米德有序體"]形式上:
#check ℝ -- ℝ : Type
#check Real.isComplete -- 完備性:每個 Cauchy 序列收斂
#check Real.archimedean -- 阿基米德性質
開發者的實用結論:你通常不需要知道 ℝ 是怎麼造的。mathlib 提供 ℝ 是「完備有序體」這一層抽象介面,就像你用 Vec<T> 不必知道記憶體佈局。但知道建構有助於理解為何某些引理(如 floor、sup 的存在)需要額外的工作。
order lattice 初步#
ℝ(以及多數有序結構)帶有 lattice 結構:每對元素有 min / max,以及 sup / inf 的存在。核心工具是反對稱性:
#check le_antisymm
-- le_antisymm : a ≤ b → b ≤ a → a = b
證明「兩個東西相等」的常用策略:證 ≤ 與 ≥ 兩個方向。這在實分析中無所不在,例如證明 Real.sqrt 的特徵性質。
nlinarith:非線性算術#
omega 只管線性;nlinarith 在 linear_combination / 乘法推理的幫助下處理部分非線性不等式:
example (a b : ℝ) (ha : 0 ≤ a) (hb : 0 ≤ b) : a * b + 1 ≤ a * a + b * b + 1 := by
nlinarith [sq_nonneg (a - b)] -- 用 (a-b)² ≥ 0 這個額外事實
example (x : ℝ) (hx : x > 1) : x * x > x := by
nlinarith [hx] -- 從 x > 1 推出 x² > x
nlinarith 的本質:把給定的非線性「事實」做線性組合,推出目標。給的提示越多,成功機率越高。
完整開發流程:從 sorry 到證明#
目標:證明對所有實數 \(x \geq 0\) ,有 \(x \leq x^2 + 1\) 。
第 1 步:骨架 + sorry
example (x : ℝ) (hx : 0 ≤ x) : x ≤ x^2 + 1 := by
sorry
sorry 是佔位符:編譯通過但帶警告(declaration uses 'sorry'),適合先定義結構。
第 2 步:想數學。 我們要證 \(x^2 - x + 1 \geq 0\) 。配方: \(x^2 - x + 1 = (x - \frac{1}{2})^2 + \frac{3}{4} > 0\) 。
第 3 步:給 nlinarith 餵提示。
example (x : ℝ) (hx : 0 ≤ x) : x ≤ x^2 + 1 := by
have h1 : (x - 1/2)^2 ≥ 0 := sq_nonneg _
nlinarith [h1]
失敗了?看錯誤訊息:nlinarith 無法只從 (x - 1/2)² ≥ 0 得到目標。展開: \((x-\frac12)^2 \ge 0\) 給出 \(x^2 - x + \frac14 \ge 0\) ,於是 \(x^2 + 1 \ge x + \frac34\) —— 應該夠了。問題可能出在 1/2 被當成整數除法。改寫:
第 4 步:修正並完成。
example (x : ℝ) (hx : 0 ≤ x) : x ≤ x^2 + 1 := by
have h1 : (x - 1 / 2 : ℝ) ^ 2 ≥ 0 := sq_nonneg _
nlinarith [h1]
(關鍵細節:型別歸屬 (... : ℝ) 確保 1/2 是實數 \(\frac{1}{2}\) 而非自然數除法截斷。這是 Lean 初學者最常見的陷阱之一。)
第 5 步:清理。 移除多餘的 have、確認無 sorry 警告、lake build 通過 —— 完成。
這個循環 —— 骨架 → 數學草稿 → 餵提示 → 讀錯誤訊息 → 修正 —— 正是人機協作證明的日常節奏,也是第 5 章讓 LLM 參與證明開發的基礎。
小結與練習#
mathlib 的集合是述詞(Set α = α → Prop),集合相等由 funext 化為逐點邏輯;單射 / 滿射是精確的全稱/存在命題;ℝ 是 Cauchy 完備化的抽象介面;le_antisymm 與 nlinarith 是有序結構下的兩大利器。
練習:
- 證明
Set.univ恆等式:a ∪ aᶜ = univ(用funext+simp)。 - 證明 \(g(n) = 2n\) 不是滿射、是單射(在 ℕ 上)。
- 證明:
(a b : ℝ) (h : a ≤ b) : min a b + max a b = a + b(先展開 min/max 的定義)。 - 用
nlinarith證明:(a b : ℝ) (ha : 0 < a) (hb : 0 < b) : a * b > 0。 - 走完整開發流程:證明
(x : ℝ) : x * x + 3 * x + 2 ≥ 0不對,改證(x : ℝ) : (x + 1) * (x + 2) = x * x + 3 * x + 2 := by ring,再用nlinarith證明(x : ℝ) : x ≤ x^2 + 2。