08.1 微分#
本節目標#
- 理解導數的極限定義 \(f'(x) = \lim_{h \to 0} \frac{f(x+h) - f(x)}{h}\) 。
- 認識 Mathlib 中的
HasDerivAt與deriv兩個核心概念。 - 會用
deriv_pow、deriv_add等引理做多項式微分。 - 會用
HasDerivAt.comp表達鏈鎖律。 - 體會實務上的證明套路:
simp開路、ring收尾。
導數的定義#
在標準微積分課程中,導數是用極限定義的:
幾何上, \(\frac{f(x+h) - f(x)}{h}\) 是過 \((x, f(x))\) 與 \((x+h, f(x+h))\) 的割線斜率, 當 \(h \to 0\) 時割線逼近切線,所以 \(f'(x)\) 就是切線斜率。
但在 Mathlib 中,你幾乎找不到這個極限的直接定義。原因很實際: \(\varepsilon\) - \(\delta\) 極限在證明中很難操作。Mathlib 採用的是一階近似(first-order approximation)的觀點:
定義:我們說 \(f'\) 是 \(f\) 在 \(x\) 處的導數,記作 HasDerivAt f f' x,若差商的 誤差在 \(h \to 0\) 時是線性小項,也就是存在一個函數 \(r\) 滿足
這個定義與極限定義等價(Mathlib 中有引理銜接兩者),但它在代數操作上好用得多: 乘積法則、鏈鎖律都能直接用代數變換證出來,而不必搬弄極限。
Mathlib 的兩層架構:HasDerivAt 與 deriv#
Mathlib 把「可微」分成兩層:
HasDerivAt f f' x:命題層,說「 \(f'\) 是 \(f\) 在 \(x\) 處的導數」。證明時用。deriv f x : 𝕜:函數層,直接取出導數的值。計算時用。
兩者由 HasDerivAt.deriv : HasDerivAt f f' x → HasDerivAt f (deriv f x) x 銜接。 另外 deriv f x 是用 Classical.choice 定義出來的:只要 \(f\) 在 \(x\) 可微, 就取那個唯一的 \(f'\) ;不可微時取 \(0\) (這是數學上無關緊要、但讓定義良定的選擇)。
open Real
-- 平方函數在任意點可微,且導數為 2x
example (x : ℝ) : HasDerivAt (fun y => y ^ 2) (2 * x) x := by
hasDerivAt (x ^ 2) 2 x -- Mathlib 的便利 tactic,本質是 mul_one 與 hasDerivAt_pow
上面用的是 Mathlib 提供的 hasDerivAt tactic;若要手寫,核心引理是 hasDerivAt_pow(或對整數指數的 HasDerivAt.pow)。
多項式微分#
多項式微分靠兩條引理組合:
open Real
example (a b : ℝ) (x : ℝ) : deriv (fun y => a * y + b) x = a := by
simp [deriv_add]
example (x : ℝ) : deriv (fun y => y ^ 3 + 2 * y) x = 3 * x ^ 2 + 2 := by
simp [deriv_add, deriv_pow] -- simp 後通常已把 deriv 全部展開
常用引理一覽(命名都很好猜):
| 引理 | 內容 |
|---|---|
deriv_id' | \(\frac{d}{dx} x = 1\) |
deriv_const | \(\frac{d}{dx} c = 0\) |
deriv_add | \((f + g)' = f' + g'\) |
deriv_mul | \((fg)' = f'g + fg'\) |
deriv_pow | \(\frac{d}{dx} x^n = n \cdot x^{n-1}\) |
deriv_const_mul | \((cf)' = c \cdot f'\) |
鏈鎖律#
鏈鎖律在 Mathlib 中是 HasDerivAt.comp:
theorem HasDerivAt.comp
(hf : HasDerivAt f f' x) (hg : HasDerivAt g g' (f x)) :
HasDerivAt (g ∘ f) (g' * f') x
注意乘法的順序是 \(g' \cdot f'\) (先外層後內層),這是因為 comp 把外層函數 g 寫在前面。複合函數在 Mathlib 中寫成 g ∘ f。
open Real
-- (x^2 + 1)^3 的導數是 3 * (x^2+1)^2 * 2x
example (x : ℝ) :
HasDerivAt (fun y => (y ^ 2 + 1) ^ 3) (3 * (x ^ 2 + 1) ^ 2 * (2 * x)) x := by
have h1 : HasDerivAt (fun y => y ^ 2 + 1) (2 * x) x := by
have hpow : HasDerivAt (fun y => y ^ 2) (2 * x) x := by
simpa using hasDerivAt_pow x 2
exact hpow.add (hasDerivAt_const x 1)
have h2 : HasDerivAt (fun u => u ^ 3) (3 * (x ^ 2 + 1) ^ 2) (x ^ 2 + 1) := by
simpa using hasDerivAt_pow (x ^ 2 + 1) 3
exact h2.comp h1
完整對照:求 \(x^2\) 的導數#
手算過程:
Lean 對照:我們直接從定義出發,用 HasDerivAt 的展開形式證明。 Mathlib 提供引理 HasDerivAt.div_const 之餘,更根本的是可以直接構造 線性近似:
open Real
-- 目標:HasDerivAt (fun y => y ^ 2) (2 * x) x
example (x : ℝ) : HasDerivAt (fun y => y ^ 2) (2 * x) x := by
-- 第一步(對應手算的展開):代數恆等式 (x+h)^2 = x^2 + 2x*h + h*h
have key : ∀ h : ℝ, (x + h) ^ 2 - x ^ 2 - (2 * x) * h = h * h := by
intro h; ring
-- 第二步(對應取極限):h*h 除以 h 是 h,趨近 0
exact HasDerivAt.of_isLittleOTVS
(fun h => key h) -- 誤差項恰好是 h*h
(isLittleO_id' (fun _ => one_ne_zero) one_mul).congr_left (by fun_prop)
實務上沒有人這樣從定義硬寫;標準做法是呼叫 hasDerivAt tactic 或組合引理。 這個對照想傳達的重點是:手算中「展開後約去 \(h\) 」的那一步,在 Lean 中變成 一個純代數的 ring 恆等式,而「 \(h \to 0\) 時 \(h^2/h \to 0\) 」這件麻煩事被 封裝在 HasDerivAt 的定義裡。這正是 Lean 式數學的日常:把解析論證化約成代數。
實務建議:simp + ring 收尾#
計算型微積分目標(例如 deriv f x = 某式)的標準流程:
- 先
simp [deriv_add, deriv_pow, ...]把deriv消化掉。 - 剩下的多是整式恆等式,用
ring收尾。 - 有分式時用
field_simp預處理,再ring。
open Real
example (x : ℝ) (hx : x ≠ 0) : deriv (fun y => y / y ^ 2) x = -1 / x ^ 2 := by
simp only [deriv_div] -- (f/g)' = (f'g - fg')/g^2
field_simp
ring
遇到 simp 不動時,先用 deriv 的計算 lemma(derive tactic 也值得一試: derive 會自動搜尋可微性與導數公式),再回頭手動組 HasDerivAt 引理。
小結與練習#
本節要點:
- 導數的極限定義在 Mathlib 中被
HasDerivAt的「一階近似」定義取代,兩者等價。 HasDerivAt f f' x是命題層(用來證明),deriv f x是函數層(用來計算)。- 多項式微分 =
deriv_add+deriv_pow;鏈鎖律 =HasDerivAt.comp。 - 實務套路:
simp消導數、ring(或field_simp+ring)收尾。
練習:
- 用
simp [deriv_add, deriv_pow]證明:對任意 \(x : \mathbb{R}\) , \(\frac{d}{dx}(x^4 - 3x) = 4x^3 - 3\) 。 - 用
HasDerivAt.comp證明 \(\sin(x^2)\) 在任意點可微,並寫出 Lean 中的導數表達式 (提示:Real.hasDerivAt_sin)。 - 從定義層次重現本節的 \(x^2\) 對照:把
key中的多項式換成 \((x+h)^3\) , 證明(x + h) ^ 3 - x ^ 3 - 3 * x ^ 2 * h = 3 * x * h ^ 2 + h ^ 3(ring即可), 並觀察誤差項仍是 \(O(h)\) 。 - 挑戰:證明
deriv (fun y => 1 / y) x = -1 / x ^ 2(提示:deriv_inv,需要 \(x \neq 0\) )。