Path: blob/master/src/solutions/05_sequence_limits.lean
292 views
import data.real.basic
import algebra.group.pi
import tuto_lib
notation `|`x`|` := abs x
/-
In this file we manipulate the elementary definition of limits of
sequences of real numbers.
mathlib has a much more general definition of limits, but here
we want to practice using the logical operators and relations
covered in the previous files.
A sequence u is a function from ℕ to ℝ, hence Lean says
u : ℕ → ℝ
The definition we'll be using is:
-- Definition of « u tends to l »
def seq_limit (u : ℕ → ℝ) (l : ℝ) : Prop :=
∀ ε > 0, ∃ N, ∀ n ≥ N, |u n - l| ≤ ε
Note the use of `∀ ε > 0, ...` which is an abbreviation of
`∀ ε, ε > 0 → ... `
In particular, a statement like `h : ∀ ε > 0, ...`
can be specialized to a given ε₀ by
`specialize h ε₀ hε₀`
where hε₀ is a proof of ε₀ > 0.
Also recall that, wherever Lean expects some proof term, we can
start a tactic mode proof using the keyword `by` (followed by curly braces
if you need more than one tactic invocation).
For instance, if the local context contains:
δ : ℝ
δ_pos : δ > 0
h : ∀ ε > 0, ...
then we can specialize h to the real number δ/2 using:
`specialize h (δ/2) (by linarith)`
where `by linarith` will provide the proof of `δ/2 > 0` expected by Lean.
We'll take this opportunity to use two new tactics:
`norm_num` will perform numerical normalization on the goal and `norm_num at h`
will do the same in assumption `h`. This will get rid of trivial calculations on numbers,
like replacing |l - l| by zero in the next exercise.
`congr'` will try to prove equalities between applications of functions by recursively
proving the arguments are the same.
For instance, if the goal is `f x + g y = f z + g t` then congr will replace it by
two goals: `x = z` and `y = t`.
You can limit the recursion depth by specifying a natural number after `congr'`.
For instance, in the above example, `congr' 1` will give new goals
`f x = f z` and `g y = g t`, which only inspect arguments of the addition and not deeper.
-/
variables (u v w : ℕ → ℝ) (l l' : ℝ)
-- If u is constant with value l then u tends to l
-- 0033
example : (∀ n, u n = l) → seq_limit u l :=
begin
-- sorry
intros h ε ε_pos,
use 0,
intros n hn,
rw h,
norm_num,
linarith,
-- sorry
end
/- When dealing with absolute values, we'll use lemmas:
abs_le {x y : ℝ} : |x| ≤ y ↔ -y ≤ x ∧ x ≤ y
abs_add (x y : ℝ) : |x + y| ≤ |x| + |y|
abs_sub_comm (x y : ℝ) : |x - y| = |y - x|
You should probably write them down on a sheet of paper that you keep at
hand since they are used in many exercises.
-/
-- Assume l > 0. Then u tends to l implies u n ≥ l/2 for large enough n
-- 0034
example (hl : l > 0) : seq_limit u l → ∃ N, ∀ n ≥ N, u n ≥ l/2 :=
begin
-- sorry
intro h,
cases h (l/2) (by linarith) with N hN,
use N,
intros n hn,
specialize hN n hn,
rw abs_le at hN,
linarith,
-- sorry
end
/-
When dealing with max, you can use
ge_max_iff (p q r) : r ≥ max p q ↔ r ≥ p ∧ r ≥ q
le_max_left p q : p ≤ max p q
le_max_right p q : q ≤ max p q
You should probably add them to the sheet of paper where you wrote
the `abs` lemmas since they are used in many exercises.
Let's see an example.
-/
-- If u tends to l and v tends l' then u+v tends to l+l'
example (hu : seq_limit u l) (hv : seq_limit v l') :
seq_limit (u + v) (l + l') :=
begin
intros ε ε_pos,
cases hu (ε/2) (by linarith) with N₁ hN₁,
cases hv (ε/2) (by linarith) with N₂ hN₂,
use max N₁ N₂,
intros n hn,
cases ge_max_iff.mp hn with hn₁ hn₂,
have fact₁ : |u n - l| ≤ ε/2,
from hN₁ n (by linarith), -- note the use of `from`.
-- This is an alias for `exact`,
-- but reads nicer in this context
have fact₂ : |v n - l'| ≤ ε/2,
from hN₂ n (by linarith),
calc
|(u + v) n - (l + l')| = |u n + v n - (l + l')| : rfl
... = |(u n - l) + (v n - l')| : by congr' 1 ; ring
... ≤ |u n - l| + |v n - l'| : by apply abs_add
... ≤ ε : by linarith,
end
/-
In the above proof, we used `have` to prepare facts for `linarith` consumption in the last line.
Since we have direct proof terms for them, we can feed them directly to `linarith` as in the next proof
of the same statement.
Another variation we introduce is rewriting using `ge_max_iff` and letting `linarith` handle the
conjunction, instead of creating two new assumptions.
-/
example (hu : seq_limit u l) (hv : seq_limit v l') :
seq_limit (u + v) (l + l') :=
begin
intros ε ε_pos,
cases hu (ε/2) (by linarith) with N₁ hN₁,
cases hv (ε/2) (by linarith) with N₂ hN₂,
use max N₁ N₂,
intros n hn,
rw ge_max_iff at hn,
calc
|(u + v) n - (l + l')| = |u n + v n - (l + l')| : rfl
... = |(u n - l) + (v n - l')| : by congr' 1 ; ring
... ≤ |u n - l| + |v n - l'| : by apply abs_add
... ≤ ε : by linarith [hN₁ n (by linarith), hN₂ n (by linarith)],
end
/- Let's do something similar: the squeezing theorem. -/
-- 0035
example (hu : seq_limit u l) (hw : seq_limit w l)
(h : ∀ n, u n ≤ v n)
(h' : ∀ n, v n ≤ w n) : seq_limit v l :=
begin
-- sorry
intros ε ε_pos,
cases hu ε ε_pos with N hN,
cases hw ε ε_pos with N' hN',
use max N N',
intros n hn,
rw ge_max_iff at hn,
specialize hN n (by linarith),
specialize hN' n (by linarith),
specialize h n,
specialize h' n,
rw abs_le at *,
split,
-- Here `linarith` can finish, but on paper we would write
calc -ε ≤ u n - l : by linarith
... ≤ v n - l : by linarith,
calc v n - l ≤ w n - l : by linarith
... ≤ ε : by linarith,
-- sorry
end
/- What about < ε? -/
-- 0036
example (u l) : seq_limit u l ↔
∀ ε > 0, ∃ N, ∀ n ≥ N, |u n - l| < ε :=
begin
-- sorry
split,
{ intros hyp ε ε_pos,
cases hyp (ε/2) (by linarith) with N hN,
use N,
intros n hn,
calc |u n - l| ≤ ε/2 : by exact hN n hn
... < ε : by linarith, },
{ intros hyp ε ε_pos,
cases hyp ε ε_pos with N hN,
use N,
intros n hn,
specialize hN n hn,
linarith, },
-- sorry
end
/- In the next exercise, we'll use
eq_of_abs_sub_le_all (x y : ℝ) : (∀ ε > 0, |x - y| ≤ ε) → x = y
-/
-- A sequence admits at most one limit
-- 0037
example : seq_limit u l → seq_limit u l' → l = l' :=
begin
-- sorry
intros hl hl',
apply eq_of_abs_sub_le_all,
intros ε ε_pos,
cases hl (ε/2) (by linarith) with N hN,
cases hl' (ε/2) (by linarith) with N' hN',
calc |l - l'| = |(l-u (max N N')) + (u (max N N') -l')| : by ring_nf
... ≤ |l - u (max N N')| + |u (max N N') - l'| : by apply abs_add
... = |u (max N N') - l| + |u (max N N') - l'| : by rw abs_sub_comm
... ≤ ε : by linarith [hN (max N N') (le_max_left _ _), hN' (max N N') (le_max_right _ _)]
-- sorry
end
/-
Let's now practice deciphering definitions before proving.
-/
def non_decreasing (u : ℕ → ℝ) := ∀ n m, n ≤ m → u n ≤ u m
def is_seq_sup (M : ℝ) (u : ℕ → ℝ) :=
(∀ n, u n ≤ M) ∧ ∀ ε > 0, ∃ n₀, u n₀ ≥ M - ε
-- 0038
example (M : ℝ) (h : is_seq_sup M u) (h' : non_decreasing u) :
seq_limit u M :=
begin
-- sorry
intros ε ε_pos,
cases h with inf_M sup_M_ep,
cases sup_M_ep ε ε_pos with n₀ hn₀,
use n₀,
intros n hn,
rw abs_le,
split; linarith [inf_M n, h' n₀ n hn],
-- sorry
end