induction'

induction'induction の Lean 3 での構文を残したバージョンです.

import Mathlib.Tactic -- 大雑把に import する


/-- `0` から `n` までの和を計算する.
多項式関数として表現する都合で,返り値は `Rat` にしてある. -/
def sum (n : Nat) : Rat :=
  match n with
  | 0 => 0
  | n + 1 => (n + 1) + sum n

example (n : Nat) : sum n = n * (n + 1) / 2 := by
  -- `n` についての帰納法で示す
  induction' n with n ih

  -- `n = 0` の場合
  case zero =>
    simp_all [sum]

  -- `0` から `n` までの自然数で成り立つと仮定する
  case succ =>
    -- `sum` の定義を展開し,帰納法の仮定を適用する
    simp [sum, ih]

    -- 後は可換環の性質から示せる
    ring

〇〇の~についての帰納法

関数適用した対象に対する帰納法も行うことができます.

以下の例は, Cantor の対関数 pair : ℕ × ℕ → ℕ に対して逆写像 unpair : ℕ → ℕ × ℕ が存在することを示しています.証明の中で pair (m, n) に対する帰納法を行っています.また,generalizing 構文を使うことで,帰納法の仮定の中の変数を全称化する工夫も行っています.

/-- `0` から `n` までの自然数の和.
多項式として表現する必要はないので,返り値は自然数. -/
def sum (n : ℕ) : ℕ :=
  match n with
  | 0 => 0
  | n + 1 => (n + 1) + sum n

@[simp]
theorem summ_zero_iff_zero {n : ℕ} : sum n = 0 ↔ n = 0 := by
  constructor <;> intro h
  case mp =>
    induction' n with n _ih <;> simp at *
    simp [sum] at h
  case mpr =>
    rw [h, sum]

/-- カントールの対関数 -/
def pair : ℕ × ℕ → ℕ
  | (m, n) => sum (m + n) + m

@[simp]
theorem pair_zero : pair 0 = 0 := by rfl

/-- カントールの対関数の逆関数 -/
def unpair : ℕ → ℕ × ℕ
  | 0 => (0, 0)
  | x + 1 =>
    let (m, n) := unpair x
    if n = 0 then
      (0, m + 1)
    else
      (m + 1, n - 1)

@[simp]
theorem unpair_zero : unpair 0 = 0 := by rfl

theorem unpair_pair_eq_id (m n : ℕ) : unpair (pair (m, n)) = (m, n) := by
  -- `x = pair (m, n)` として `x` に対する帰納法を利用する
  induction' h : pair (m, n) with x ih generalizing m n
  all_goals simp at *

  -- `pair (m, n) = 0` のとき
  case zero =>
    -- `pair` の定義から明らか.
    simp [pair] at h
    aesop

  -- `pair (m, n) = x + 1` のとき
  case succ =>
    -- `m` がゼロかそうでないかで場合分けする
    match m with

    -- `m = 0` のとき
    | 0 =>
      -- `pair (0, n) = x + 1` により `n > 0` が成り立つ.
      have npos : n > 0 := by
        by_contra!; simp at this
        simp [this] at h
        contradiction

      -- よって `n = (n - 1) + 1` であり,
      replace npos : n = (n - 1) + 1 := by omega
      have : sum n = sum (n - 1) + n := by
        nth_rw 1 [npos]
        dsimp [sum]
        omega

      -- `pair (n - 1, 0) = x` が成り立つ.
      replace : pair (n - 1, 0) = x := by
        dsimp [pair] at h ⊢
        simp at h
        rw [this] at h
        omega

      -- 後は帰納法の仮定から従う.
      specialize ih (n-1) 0 this
      simp [unpair, ih]
      symm; assumption

    -- `m = m' + 1` のとき
    | m' + 1 =>
      -- `pair` の定義から `pair (m', n + 1) = x` が成り立つ.
      have : pair (m', n + 1) = x := by
        simp [pair] at h ⊢
        rw [show m' + 1 + n = m' + (n + 1) from by ac_rfl] at h
        omega

      -- 後は帰納法の仮定から従う.
      specialize ih m' (n + 1) this
      simp [unpair, ih]

完全帰納法

時には, より強い帰納法が必要なこともあります. 強い帰納法とは, たとえば

  • ∀ n, (∀ k < n, P (k)) → P (n) を示す
  • したがって ∀ n, P (n) である

という形式で表されるような帰納法のことです. これは超限帰納法の特別な場合で,完全帰納法や累積帰納法とも呼ばれます.

/-- フィボナッチ数列の通常の定義をそのまま Lean の関数として書いたもの -/
def fibonacci : Nat → Nat
| 0 => 0
| 1 => 1
| n + 2 => fibonacci n + fibonacci (n + 1)

/-- フィボナッチ数列の線形時間の実装 -/
def fib (n : Nat) : Nat :=
  (loop n).1
where
  loop : Nat → Nat × Nat
    | 0 => (0, 1)
    | n + 1 =>
      let p := loop n
      (p.2, p.1 + p.2)

/-- `fib` が `fibonacci` と同じ漸化式を満たすことを証明する -/
@[simp]
theorem fib_add (n : Nat) : fib n + fib (n + 1) = fib (n + 2) := by rfl

/-- `fibonacci` と `fib` は同じ結果を返す -/
example : fibonacci = fib := by
  -- 関数が等しいことを示すので,引数 `n` が与えられたとする
  ext n

  -- `n` についての強い帰納法で示す
  induction' n using Nat.strong_induction_on with n ih
  match n with
  | 0 => rfl
  | 1 => rfl
  | n + 2 => simp_all [fibonacci]