Imports
import Mathlib
open Filteropen Asymptotics

3.1. Asymptotic Notation

CLRS-compatible wrappers for mathlib's filter-based asymptotics on ℕ → ℝ. Proves equivalence between the CLRS discrete definition and the filter definition, plus standard algebraic properties: the Θ characterization with a single shared threshold, the o/ω and O/Ω dualities, and the little-o closure properties (additivity, products, composition) used in the growth estimates of §3.2.

namespace CLRSnamespace Chapter03

Wrapper definitions

def isBigO (f g : ) : Prop := f =O[atTop] gdef isBigOmega (f g : ) : Prop := g =O[atTop] fdef isBigTheta (f g : ) : Prop := isBigO f g isBigOmega f gdef isLittleO (f g : ) : Prop := f =o[atTop] gdef isLittleOmega (f g : ) : Prop := g =o[atTop] f

Equivalence with CLRS discrete definition

theorem isBigO_iff (f g : ) : isBigO f g (c : ), c > 0 (n₀ : ), n, n n₀ |f n| c * |g n| := by unfold isBigO rw [IsBigO_def] constructor · rintro c, hc rcases IsBigOWith.exists_pos hc with c', hc_pos, hc' have hevent := (isBigOWith_iff.mp hc') have hevent' : ∀ᶠ n in atTop, |f n| c' * |g n| := by simpa [Real.norm_eq_abs] using hevent rw [Filter.eventually_atTop] at hevent' rcases hevent' with n₀, hn₀ exact c', hc_pos, n₀, hn₀ · rintro c, hc_pos, n₀, hn₀ have hevent : ∀ᶠ n in atTop, |f n| c * |g n| := by rw [Filter.eventually_atTop] exact n₀, hn₀ have hevent' : ∀ᶠ n in atTop, f n c * g n := by simpa [Real.norm_eq_abs] using hevent have hOwith : IsBigOWith c atTop f g := isBigOWith_iff.mpr hevent' exact c, hOwith theorem isLittleO_iff (f g : ) : isLittleO f g (c : ), c > 0 (n₀ : ), n, n n₀ |f n| c * |g n| := by unfold isLittleO rw [isLittleO_iff_forall_isBigOWith] constructor · intro h c hc_pos have hOwith : IsBigOWith c atTop f g := h hc_pos have hevent := (isBigOWith_iff.mp hOwith) have hevent' : ∀ᶠ n in atTop, |f n| c * |g n| := by simpa [Real.norm_eq_abs] using hevent rw [Filter.eventually_atTop] at hevent' rcases hevent' with n₀, hn₀ exact n₀, hn₀ · intro h c hc_pos rcases h c hc_pos with n₀, hn₀ have hevent : ∀ᶠ n in atTop, |f n| c * |g n| := by rw [Filter.eventually_atTop] exact n₀, hn₀ have hevent' : ∀ᶠ n in atTop, f n c * g n := by simpa [Real.norm_eq_abs] using hevent exact isBigOWith_iff.mpr hevent' theorem isBigOmega_iff (f g : ) : isBigOmega f g (c : ), c > 0 (n₀ : ), n, n n₀ c * |g n| |f n| := by -- isBigOmega f g = isBigO g f, and isBigO_iff g f gives -- isBigO g f ↔ ∃ c>0, n₀, ∀ n≥n₀, |g n| ≤ c * |f n| -- We prove this RHS is equivalent to -- ∃ c>0, n₀, ∀ n≥n₀, c * |g n| ≤ |f n| -- by exchanging c ↔ c⁻¹. have h_base := isBigO_iff g f -- isBigOmega f g = isBigO g f definitionally -- Now the goal is: isBigO g f ↔ ∃ c>0, n₀, ∀ n≥n₀, c * |g n| ≤ |f n| -- But h_base says: isBigO g f ↔ ∃ c>0, n₀, ∀ n≥n₀, |g n| ≤ c * |f n| -- So it suffices to show the two RHSs are equivalent. constructor · -- From isBigO g f, get ∃ c>0, n₀, ∀ n≥n₀, |g n| ≤ c * |f n| -- Transform to ∃ c'>0, n₀, ∀ n≥n₀, c' * |g n| ≤ |f n| via c' = c⁻¹ intro h_isO rcases h_base.mp h_isO with c, hc_pos, n₀, hn₀ have hc_ne_zero : c 0 := by linarith refine c⁻¹, inv_pos.mpr hc_pos, n₀, λ n hn => ?_ have hineq := hn₀ n hn calc c⁻¹ * |g n| c⁻¹ * (c * |f n|) := by gcongr _ = (c⁻¹ * c) * |f n| := by ring _ = 1 * |f n| := by field_simp [hc_ne_zero] _ = |f n| := by simp · intro h_omega rcases h_omega with c, hc_pos, n₀, hn₀ have hc_ne_zero : c 0 := by linarith -- Need to show isBigO g f, i.e. ∃ c'>0, n₀, ∀ n≥n₀, |g n| ≤ c' * |f n| -- Using c' = c⁻¹ apply h_base.mpr refine c⁻¹, inv_pos.mpr hc_pos, n₀, λ n hn => ?_ have hineq := hn₀ n hn calc |g n| = (c⁻¹ * c) * |g n| := by field_simp [hc_ne_zero] _ = c⁻¹ * (c * |g n|) := by ring _ c⁻¹ * |f n| := by gcongr theorem isLittleOmega_iff (f g : ) : isLittleOmega f g (c : ), c > 0 (n₀ : ), n, n n₀ c * |g n| |f n| := by -- isLittleOmega f g = isLittleO g f -- isLittleO_iff g f says: isLittleO g f ↔ ∀ c>0, ∃ n₀, |g n| ≤ c * |f n| -- We need to show the RHS is equivalent to ∀ c>0, c * |g n| ≤ |f n| -- via exchanging c ↔ c⁻¹. have h_base := isLittleO_iff g f -- isLittleOmega f g = isLittleO g f definitionally constructor · intro h_o c hc have hc_inv_pos : c⁻¹ > 0 := inv_pos.mpr hc rcases (h_base.mp h_o) c⁻¹ hc_inv_pos with n₀, hn₀ have hc_ne_zero : c 0 := by linarith refine n₀, λ n hn => ?_ have hineq := hn₀ n hn calc c * |g n| c * (c⁻¹ * |f n|) := by gcongr _ = (c * c⁻¹) * |f n| := by ring _ = 1 * |f n| := by field_simp [hc_ne_zero] _ = |f n| := by simp · intro h_forall apply h_base.mpr intro c' hc'_pos have hc_inv_pos : c'⁻¹ > 0 := inv_pos.mpr hc'_pos rcases h_forall c'⁻¹ hc_inv_pos with n₀, hn₀ have hc_ne_zero : c' 0 := by linarith refine n₀, λ n hn => ?_ have hineq := hn₀ n hn calc |g n| = c' * (c'⁻¹ * |g n|) := by field_simp [hc_ne_zero] _ c' * |f n| := by gcongr

Algebraic properties

theorem isBigO_refl (f : ) : isBigO f f := by unfold isBigO exact Asymptotics.isBigO_refl f atToptheorem isBigOmega_refl (f : ) : isBigOmega f f := isBigO_refl ftheorem isBigTheta_refl (f : ) : isBigTheta f f := isBigO_refl f, isBigOmega_refl ftheorem isBigO_trans {f g h : } (hfg : isBigO f g) (hgh : isBigO g h) : isBigO f h := by unfold isBigO at hfg hgh exact IsBigO.trans hfg hghtheorem isBigOmega_trans {f g h : } (hfg : isBigOmega f g) (hgh : isBigOmega g h) : isBigOmega f h := by unfold isBigOmega at hfg hgh exact IsBigO.trans hgh hfgtheorem isBigTheta_symm {f g : } (h : isBigTheta f g) : isBigTheta g f := h.2, h.1theorem isBigTheta_trans {f g h : } (hfg : isBigTheta f g) (hgh : isBigTheta g h) : isBigTheta f h := isBigO_trans hfg.1 hgh.1, isBigOmega_trans hfg.2 hgh.2theorem isBigO_add {f₁ f₂ g : } (h₁ : isBigO f₁ g) (h₂ : isBigO f₂ g) : isBigO (λ n => f₁ n + f₂ n) g := by unfold isBigO at h₁ h₂ exact IsBigO.add h₁ h₂theorem isBigTheta_iff (f g : ) : isBigTheta f g isBigO f g isBigO g f := by simp [isBigTheta, isBigOmega, isBigO]

Shared threshold for Θ and dualities

Theorem (Θ with a shared threshold). f = Θ(g) iff there exist positive constants c₁, c₂ and a single threshold n₀ such that for all n ≥ n₀, c₁ * |g n| ≤ |f n| ≤ c₂ * |g n|.

This is the two-sided witness characterization of Θ-notation from CLRS §3.1; the two independent witnesses are combined by taking the maximum of their thresholds.

theorem isBigTheta_iff_sharedThreshold (f g : ) : isBigTheta f g c₁ c₂ : , 0 < c₁ 0 < c₂ n₀ : , n, n₀ n c₁ * |g n| |f n| |f n| c₂ * |g n| := by rw [isBigTheta_iff] rw [show isBigO g f isBigOmega f g by simp [isBigO, isBigOmega]] rw [isBigO_iff, isBigOmega_iff] constructor · rintro hO, rcases hO with c₂, hc₂, n₀₂, h₂ rcases with c₁, hc₁, n₀₁, h₁ refine c₁, c₂, hc₁, hc₂, max n₀₁ n₀₂, ?_ intro n hn exact h₁ n (le_trans (le_max_left n₀₁ n₀₂) hn), h₂ n (le_trans (le_max_right n₀₁ n₀₂) hn) · rintro c₁, c₂, hc₁, hc₂, n₀, h constructor · refine c₂, hc₂, n₀, ?_ intro n hn exact (h n hn).2 · refine c₁, hc₁, n₀, ?_ intro n hn exact (h n hn).1

Lemma (duality of o and ω). f = o(g) iff g = ω(f) (CLRS §3.1, with the convention isLittleOmega f g := isLittleO g f).

theorem isLittleO_reciprocal (f g : ) : isLittleO f g isLittleOmega g f := by simp [isLittleO, isLittleOmega]

Lemma (duality of O and Ω). f = O(g) iff g = Ω(f) (CLRS §3.1, with the convention isBigOmega f g := isBigO g f).

theorem isBigO_reciprocal (f g : ) : isBigO f g isBigOmega g f := by simp [isBigO, isBigOmega]

Little-o and little-omega algebra

Lemma. Little-o implies big-O: if f = o(g) then f = O(g) (CLRS §3.1).

theorem isLittleO_isBigO {f g : } : isLittleO f g isBigO f g := by intro h unfold isLittleO at h unfold isBigO exact h.isBigO

Lemma (additivity of o). If f₁ = o(g) and f₂ = o(g), then f₁ + f₂ = o(g) (CLRS §3.1).

theorem isLittleO_add {f₁ f₂ g : } : isLittleO f₁ g isLittleO f₂ g isLittleO (fun n => f₁ n + f₂ n) g := by intro h₁ h₂ unfold isLittleO at h₁ h₂ exact h₁.add h₂

Lemma (product rule). If f₁ = o(g₁) and f₂ = O(g₂), then f₁ * f₂ = o(g₁ * g₂) (CLRS §3.1).

theorem isLittleO_mul {f₁ g₁ f₂ g₂ : } : isLittleO f₁ g₁ isBigO f₂ g₂ isLittleO (fun n => f₁ n * f₂ n) (fun n => g₁ n * g₂ n) := by intro h₁ h₂ unfold isLittleO at h₁ unfold isBigO at h₂ exact h₁.mul_isBigO h₂

Lemma (composition of o). If h : ℕ → ℕ tends to infinity and f = o(g), then f ∘ h = o(g ∘ h). This is the asymptotic analogue of substituting h n for n, as used in the growth estimates of CLRS §3.1/§3.2.

theorem isLittleO_comp {f g : } {h : } (hh : Tendsto h atTop atTop) : isLittleO f g isLittleO (fun n => f (h n)) (fun n => g (h n)) := by intro hfg unfold isLittleO at hfg exact hfg.comp_tendsto hh

Lemma (scaling ω). If c > 0 and f = ω(g), then c * f = ω(g) (CLRS §3.1: positive constant factors are irrelevant in asymptotic notation).

theorem isLittleOmega_scale {f g : } {c : } (hc : 0 < c) : isLittleOmega f g isLittleOmega (fun n => c * f n) g := by intro h unfold isLittleOmega at h exact h.const_mul_right (ne_of_gt hc)

Lemma (ω survives addition of a dominated term). If f = ω(g) and h = O(g), then f + h = ω(g) (CLRS §3.1: adding an O(g) term does not change the ω(g) lower bound).

theorem isLittleOmega_add_dominated {f g h : } : isLittleOmega f g isBigO h g isLittleOmega (fun n => f n + h n) g := by intro hfg hh have h_ho : isLittleO h f := by unfold isBigO at hh unfold isLittleO exact hh.trans_isLittleO hfg rw [isLittleOmega_iff] at hfg rw [isLittleO_iff] at h_ho rw [isLittleOmega_iff] intro c hc rcases hfg (2 * c) (by positivity) with n₁, hn₁ rcases h_ho (1 / 2) (by norm_num) with n₂, hn₂ refine max n₁ n₂, ?_ intro n hn have hn₁' : n₁ n := le_trans (le_max_left n₁ n₂) hn have hn₂' : n₂ n := le_trans (le_max_right n₁ n₂) hn have hg : (2 * c) * |g n| |f n| := hn₁ n hn₁' have hh' : |h n| (1 / 2) * |f n| := hn₂ n hn₂' have htri : |f n| - |h n| |f n + h n| := by have h₁ : |f n| |f n + h n| + |h n| := by calc |f n| = |(f n + h n) + (-h n)| := by congr 1; ring _ |f n + h n| + |(-h n)| := abs_add_le (f n + h n) (-(h n)) _ = |f n + h n| + |h n| := by rw [abs_neg] linarith calc c * |g n| = (1 / 2) * (2 * c * |g n|) := by ring _ (1 / 2) * |f n| := by gcongr _ |f n| - |h n| := by linarith _ |f n + h n| := htri
end Chapter03end CLRS