lean-ja / lean-by-example

コード例で学ぶ Lean 言語
https://lean-ja.github.io/lean-by-example/
MIT License
48 stars 7 forks source link

演習問題: Beatty sequences に対する Rayleigh (レイリー)の定理 #784

Open Seasawher opened 2 months ago

Seasawher commented 2 months ago

無理数の定義からわりとすぐに従う上に、無理数を定義するのに必要な実数の闇にノータッチなまま話を進められそう。 しかも非自明。

Seasawher commented 1 month ago

途中経過

良い感じに難しくてちょうどいいかもしれない

参考:レイリーの定理とその自然な証明

/- # Rayleigh の定理 -/

import Mathlib.Tactic
import Mathlib.Data.Real.Irrational
import Mathlib.Data.Set.Operations

set_option autoImplicit false
set_option relaxedAutoImplicit false

/-- 正の自然数全体 -/
def PosNat : Set ℕ := { n : ℕ | 0 < n }

/-- 実数 x の正の自然数倍を整数に丸めたものからなる部分集合 -/
def beatty (x : ℝ) : Set ℕ := { ⌊n * x⌋₊ | n ∈ PosNat}

/-- beatty が PosNat の分割を与えるという命題 -/
def beatty_partition (r s : ℝ) : Prop := beatty r ∪ beatty s = PosNat ∧ beatty r ∩ beatty s = ∅

variable (r s : ℝ) (rirr : Irrational r) (sirr : Irrational s) (rpos : r > 0) (spos : s > 0)

-- `variable` で宣言した変数を引数に加えるためのコマンド
include rirr sirr rpos spos

/-- レイリーの定理 -/
theorem rayleigh_mp (h : 1/r + 1/s = 1) : beatty_partition r s := by
  dsimp [beatty_partition]
  refine ⟨?cover, ?disjoint⟩

  -- 重複がないことを示す
  case disjoint =>
    -- 両方の集合に属している要素があったと仮定して矛盾を示せばよい。
    suffices ¬ (∃ x, x ∈ beatty r ∧ x ∈ beatty s) from by
      aesop

    -- `x` が両方の集合に属していると仮定する。
    intro ⟨x, ⟨m, mpos, hr⟩, ⟨n, npos, hs⟩⟩

    -- ヒント:この命題は使用しても良い
    #check Nat.floor_eq_iff

    -- 仮定から、ある自然数 `m > 0` が存在して
    -- `x ≤ m * r < x + 1` が成り立つ。
    replace ⟨mpos, «x≤mr», «mr<x+1»⟩ : 0 < m ∧ x ≤ m * r ∧ m * r < x + 1 := by
      rw [Nat.floor_eq_iff (show 0 ≤ ↑m * r from by positivity)] at hr
      simp_all [PosNat]
    clear hr

    -- 同様に、ある自然数 `n > 0` が存在して
    -- `x ≤ n * s < x + 1` が成り立つ。
    replace ⟨npos, «x≤ns», «ns<x+1»⟩ : 0 < n ∧ x ≤ n * s ∧ n * s < x + 1 := by
      rw [Nat.floor_eq_iff (show 0 ≤ ↑n * s from by positivity)] at hs
      simp_all [PosNat]
    clear hs

    -- ヒント:この命題は使用してOK
    #check irrational_iff_ne_rational

    -- `r` は無理数なので狭義の不等号が成り立つ
    have «x<mr» : x < m * r := by
      -- `x ≠ m * r` を示せば十分。
      suffices ¬ (x = m * r) from by
        exact lt_of_le_of_ne «x≤mr» this

      -- `x = m * r` と仮定して矛盾を示そう。
      intro hr

      -- このとき `r = x / m` が成り立つので、
      -- `r` は無理数ではないということになる。
      have : ¬ Irrational r := by
        replace hr : r = x / m := by field_simp [hr]
        aesop

      -- これは矛盾。
      contradiction

    -- `s` も無理数なので狭義の不等号が成り立つ
    have «x<ns» : x < n * s := by
      -- `x ≠ n * s` を示せば十分。
      suffices ¬ (x = n * s) from by
        exact lt_of_le_of_ne «x≤ns» this

      -- `x = n * s` と仮定して矛盾を示そう。
      intro hs

      -- このとき `s = x / n` が成り立つので、
      -- `s` は無理数ではないということになる。
      have : ¬ Irrational s := by
        replace hs : s = x / n := by field_simp [hs]
        aesop

      -- これは矛盾。
      contradiction

    -- したがって `x < m + n < x + 1` が成り立つことになる。
    have ⟨left, right⟩ : (x : ℝ) < m + n ∧ m + n < (x + 1 : ℝ) := by
      have left : (x : ℝ) < m + n := calc
        x = x * (1/r + 1/s) := by rw [h]; simp
        _ = x / r + x / s := by ring
        _ < (m * r) / r + (n * s) / s := by gcongr
        _ = m + n := by field_simp

      have right : m + n < (x + 1 : ℝ) := calc
        m + n = (m * r) / r + (n * s) / s := by field_simp
        _ < (x + 1) / r + (x + 1) / s := by gcongr
        _ = (x + 1) * (1 / r + 1 / s) := by ring
        _ = x + 1 := by rw [h]; simp

      simp_all
    clear * - left right

    -- `x` と `m + n` はともに自然数なので、これは矛盾である。
    have : False := by
      replace left : x + 1 ≤ m + n := by
        norm_cast at left
      replace right : m + n ≤ x := by
        norm_cast at right
        omega
      omega
    contradiction

  -- どちらかの数列には必ず登場することを示す
  case cover =>
    sorry
Seasawher commented 1 month ago

beatty r ∩ beatty s = ∅ は本来示すべきことより少し弱い。集合として重複がないのではなくて、数列として重複がない。

つまり関数 n => Beatty n が単射であることの証明も必要だし、逆も成り立つからそれも必要

Seasawher commented 1 month ago

完成した。参考:レイリーの定理とその自然な証明

/- # Rayleigh の定理 -/

import Mathlib.Tactic
import Mathlib.Data.Real.Irrational

set_option autoImplicit false
set_option relaxedAutoImplicit false

/-- 正の自然数全体 -/
def PosNat : Set ℕ := { n : ℕ | 0 < n }

/-- 無理数は、正の自然数を掛けても自然数にならない -/
lemma irrational_of_posnat_mul {r : ℝ} (rirr : Irrational r) : ∀ m n : ℕ, m > 0 → (n : ℝ) ≠ m * r := by
  -- `m > 0` と `n` が存在して `m * r = n` が成り立つと仮定する。
  intro m n mpos h

  -- このとき `r = n / m` が成り立つ。
  have : r = n / m := by field_simp [h]

  -- これは `r` が無理数であることに矛盾する。
  simp [this] at rirr

/-- 実数 r の正の自然数倍を整数に丸めたものからなる部分集合 -/
def Beatty (r : ℝ) : Set ℕ := { ⌊n * r⌋₊ | n ∈ PosNat}

/-- `x ∉ Beatty r` ならば、`x = m * r` となる正の自然数は存在しない -/
lemma of_not_mem_beatty {x : ℕ} {r : ℝ} (nmem : x ∉ Beatty r) :
    ∀ m : ℕ, m > 0 → (x : ℝ) ≠ m * r := by
  intro m mpos h
  have : x ∈ Beatty r := by
    exists m
    simp_all [←h, PosNat]
  contradiction

/-- Beatty が PosNat の分割を与えるという命題 -/
def beattyPartition (r s : ℝ) : Prop := Beatty r ∪ Beatty s = PosNat ∧ Beatty r ∩ Beatty s = ∅

/-- 自然数 `x > 0` が `r` の Beatty 数列に含まれないならば、どこかで「追い越されて」いる。
つまり、ある `m : Nat` が存在して `m * r < x` かつ `x + 1 ≤ (m + 1) * r` を満たす。 -/
lemma of_beatty_missing {x : ℕ} {r : ℝ} (xpos : x > 0) (rpos : r > 0) (nmem : x ∉ Beatty r) :
    ∃ m : ℕ, m * r < x ∧ x + 1 ≤ (m + 1) * r := by
  -- `r > 0` なので、`m` を大きくしていくといつか必ず `m * r < x` は成り立たなくなる。
  -- そこで `m * r < x` が成り立つような最大の `m` を取り、`M` とする。
  let M := ⌊x / r⌋₊

  -- この `M` が所望の条件を満たすことを示そう。
  exists M
  constructor

  -- まず `M` の定義と `x` が Beatty 数列に含まれないことから
  -- `M * r < x` が成り立つことを示そう。
  case left =>
    -- 技術的な補題として `0 ≤ x / r` が必要なので示す。
    have lem : 0 ≤ x / r := by positivity

    -- このとき `M * r ≤ x` が成り立つ。
    have le : M * r ≤ x := calc
      _ = ⌊x / r⌋₊ * r := by dsimp [M]
      _ ≤ (x / r) * r := by simp_all [Nat.floor_le lem]
      _ ≤ x := by field_simp

    -- `x ∉ Beatty r` なので等号は成立しない。
    -- したがって、`M * r < x` が成り立つ。
    replace lt : M * r < x := by
      -- 背理法で示す。仮に等号が成立したとする
      suffices ¬ (M * r = x) from by
        exact lt_of_le_of_ne le this
      intro hx

      -- 補題により、`M > 0` を示せばよい。
      suffices M > 0 from by
        have lem := @of_not_mem_beatty (x := x) (r := r) nmem M this
        simp_all

      -- これは `x > 0` より従う。
      rify at xpos ⊢
      nlinarith

    exact lt

  -- 次に `x + 1 ≤ (M + 1) * r` が成り立つことを示そう。
  case right =>
    -- 技術的な補題として `(M + 1) * r ≥ 0` が必要なので示す。
    have lem : (M + 1) * r ≥ 0 := by positivity

    -- M の定義から `x < (M + 1) * r` が成り立つ。
    have : x < (M + 1) * r := calc
      _ = (x / r) * r := by field_simp
      _ < (⌊x / r⌋₊ + 1) * r := by gcongr; exact Nat.lt_floor_add_one (↑x / r)
      _ = (M + 1) * r := by dsimp [M]

    -- `x` は自然数なので床関数の定義により `x ≤ ⌊(M + 1) * r⌋₊` が成り立つ。
    replace : x ≤ ⌊(M + 1) * r⌋₊ := calc
      _ = ⌊x⌋₊ := by simp
      _ = ⌊(x : ℝ)⌋₊ := by simp
      _ ≤ ⌊(M + 1) * r⌋₊ := by gcongr

    -- `x ∉ Beatty r` なので等号は成立しない。
    replace : x < ⌊(M + 1) * r⌋₊ := by
      -- 等号不成立を示せばよい。
      suffices goal : ¬ (x = ⌊(M + 1) * r⌋₊) from by
        exact lt_of_le_of_ne this goal

      -- 仮に等号が成立したとする。
      intro hx

      -- このとき `x ∈ Beatty r` となる。
      have mem : x ∈ Beatty r := by
        exists (M + 1)
        simp_all [PosNat]

      -- これは矛盾である。
      contradiction

    -- `x` は自然数なので、`x + 1 ≤ ⌊(M + 1) * r⌋₊` が成り立つ。
    replace : x + 1 ≤ ⌊(↑M + 1) * r⌋₊ := by
      exact this

    -- したがって示すべきことが言える。
    replace : (x : ℝ) + 1 ≤ (↑M + 1) * r := calc
      _ ≤ (⌊(M + 1) * r⌋₊ : ℝ) := by norm_cast at this ⊢
      _ ≤ (M + 1) * r := by exact Nat.floor_le lem
    exact this

-- 以降 `r, s` は正の実数であると仮定する
variable {r s : ℝ} (rpos : r > 0) (spos : s > 0)
include rpos spos

/-- `1/r + 1/s = 1` の関係にある実数は、1 より大きい -/
lemma gt_of_conjugate (h : 1/r + 1/s = 1) : r > 1 := calc
  r = r * (1 / r + 1 / s) := by rw [h]; simp
  _ = r / r + r / s := by ring
  _ = 1 + r / s := by field_simp
  _ > 1 := by linarith [show r / s > 0 from by positivity]

/-- `1/r + 1/s = 1` の関係にある実数から生成される Beatty 数列は正の自然数からなる -/
lemma beatty_in_posnat (h : 1/r + 1/s = 1) : Beatty r ⊆ PosNat := by
  -- `x : Nat` が Beatty r に属していると仮定する。
  -- このとき定義から、自然数 `n > 0` が存在して
  -- `⌊n * r⌋₊ = x` が成り立つ。
  intro x ⟨n, npos, hx⟩
  dsimp [PosNat] at npos

  -- このとき `n * r > 1` が成り立っている。
  have : (n : ℝ) * r > 1 := calc
    _ ≥ 1 * r := by gcongr; norm_cast
    _ = r := by simp
    _ > 1 := by exact gt_of_conjugate rpos spos h
  clear h rpos spos npos

  -- したがって `⌊n * r⌋₊ = x` も 1 以上であり、正の自然数である。
  exact show x > 0 from calc
    _ = ⌊n * r⌋₊ := by rw [hx]
    _ ≥ ⌊(1 : ℝ)⌋₊ := by gcongr
    _ = (1 : ℕ) := by simp
    _ > 0 := by simp

/-- レイリーの定理(の弱いバージョン) -/
theorem rayleigh_mp (rirr : Irrational r) (sirr : Irrational s)
    (h : 1/r + 1/s = 1) : beattyPartition r s := by
  dsimp [beattyPartition]

  refine ⟨?cover, ?disjoint⟩

  -- 重複がないことを示す
  case disjoint =>
    -- 両方の集合に属している要素があったと仮定して矛盾を示せばよい。
    suffices ¬ (∃ x, x ∈ Beatty r ∧ x ∈ Beatty s) from by
      aesop

    -- `x` が両方の集合に属していると仮定する。
    intro ⟨x, ⟨m, mpos, hr⟩, ⟨n, npos, hs⟩⟩

    -- 仮定から、ある自然数 `m > 0` が存在して
    -- `x ≤ m * r < x + 1` が成り立つ。
    replace ⟨mpos, «x≤mr», «mr<x+1»⟩ : 0 < m ∧ x ≤ m * r ∧ m * r < x + 1 := by
      rw [Nat.floor_eq_iff (show 0 ≤ ↑m * r from by positivity)] at hr
      simp_all [PosNat]
    clear hr

    -- 同様に、ある自然数 `n > 0` が存在して
    -- `x ≤ n * s < x + 1` が成り立つ。
    replace ⟨npos, «x≤ns», «ns<x+1»⟩ : 0 < n ∧ x ≤ n * s ∧ n * s < x + 1 := by
      rw [Nat.floor_eq_iff (show 0 ≤ ↑n * s from by positivity)] at hs
      simp_all [PosNat]
    clear hs

    -- `r` は無理数なので狭義の不等号が成り立つ
    have «x<mr» : x < m * r := by
      -- `x ≠ m * r` を示せば十分。
      suffices (x : ℝ) ≠ m * r from by
        exact lt_of_le_of_ne «x≤mr» this
      -- 後は補題から従う。
      have := @irrational_of_posnat_mul r rirr m x mpos
      simp_all

    -- 同様に `s` も無理数なので狭義の不等号が成り立つ
    have «x<ns» : x < n * s := by
      suffices (x : ℝ) ≠ n * s from by
        exact lt_of_le_of_ne «x≤ns» this
      have := @irrational_of_posnat_mul s sirr n x npos
      simp_all

    -- したがって `x < m + n < x + 1` が成り立つことになる。
    have ⟨left, right⟩ : (x : ℝ) < m + n ∧ m + n < (x : ℝ) + 1 := by
      have left : (x : ℝ) < m + n := calc
        _ = x * (1/r + 1/s) := by rw [h]; simp
        _ = x / r + x / s := by ring
        _ < (m * r) / r + (n * s) / s := by gcongr
        _ = m + n := by field_simp

      have right : m + n < (x : ℝ) + 1 := calc
        _ = (m * r) / r + (n * s) / s := by field_simp
        _ < (x + 1) / r + (x + 1) / s := by gcongr
        _ = (x + 1) * (1 / r + 1 / s) := by ring
        _ = x + 1 := by rw [h]; simp

      simp_all
    clear * - left right

    -- `x` と `m + n` はともに自然数なので、これは矛盾である。
    have : False := by
      norm_cast at left right
      omega
    contradiction

  -- 全体をちょうど覆っていることを示す
  case cover =>
    -- `x : Nat` が与えられたとして、
    -- `0 < x` と数列に登場することが同値であることを示す。
    ext x
    constructor <;> intro hx

    -- 数列に登場する数は正の自然数であることを示したい。
    case mp =>
      have hr := beatty_in_posnat rpos spos h
      have hs := beatty_in_posnat spos rpos (by linarith)
      aesop

    -- 正の自然数は数列に登場することを示したい。
    case mpr =>
      -- 背理法で示す。
      by_contra hc

      -- `x` がどちらにも登場しないと仮定して矛盾を示せばよい。
      replace ⟨hr, hs⟩ : x ∉ Beatty r ∧ x ∉ Beatty s := by simp_all
      clear hc

      -- 補題から、ある `m, n : ℕ` が存在して
      -- `m * r < x`, `x + 1 ≤ (m + 1) * r` かつ
      -- `n * s < x`, `x + 1 ≤ (n + 1) * s` を満たす。
      have ⟨m, mleft, mright⟩ := @of_beatty_missing x r (by simpa using hx) rpos hr
      have ⟨n, nleft, nright⟩ := @of_beatty_missing x s (by simpa using hx) spos hs
      clear hr hs

      -- `r` は無理数なので等号は成立しない。
      replace mright : x + 1 < (m + 1) * r := by
        have := @irrational_of_posnat_mul r rirr (m + 1) (x + 1) (by simp)
        suffices goal : ¬ (x + 1 = (m + 1) * r) from by
          exact lt_of_le_of_ne mright goal
        simp_all
      clear rirr

      -- 同様に `s` も無理数なので等号は成立しない。
      replace nright : x + 1 < (n + 1) * s := by
        have := @irrational_of_posnat_mul s sirr (n + 1) (x + 1) (by simp)
        suffices goal : ¬ (x + 1 = (n + 1) * s) from by
          exact lt_of_le_of_ne nright goal
        simp_all
      clear sirr

      -- このとき `m + n < x` が成り立つ。
      have left : m + n < (x : ℝ) := calc
        _ = (m * r) / r + (n * s) / s := by field_simp
        _ < x / r + x / s := by gcongr
        _ = x * (1 / r + 1 / s) := by ring
        _ = x := by rw [h]; simp

      -- また `m + n + 1 > x` が成り立つ。
      have right : m + n + 1 > (x : ℝ) := by
        have : (m + 1) + (n + 1) > (x : ℝ) + 1 := calc
          _ = ((m + 1) * r) / r + ((n + 1) * s) / s := by field_simp
          _ > (x + 1) / r + (x + 1) / s := by gcongr
          _ = (x + 1) * (1 / r + 1 / s) := by ring
          _ = x + 1 := by rw [h]; simp
        clear * - this

        -- したがって `m + n + 1 > x` が成り立つ。
        norm_cast at this ⊢
        omega

      clear * - left right

      -- `m + n` と `x` はともに自然数なので、これは矛盾である。
      have : False := by
        norm_cast at left right
        omega
      contradiction
Seasawher commented 1 month ago

Mathlib にまだないのかと思いきや、あった

https://leanprover-community.github.io/mathlib4_docs/Mathlib/NumberTheory/Rayleigh.html

Seasawher commented 1 month ago

現状のコードは繰り返しが多すぎて演習問題にはできないので、リファクタリングが必要