Skip to content
Browse chapters

Chapter 4 — Divide-and-Conquer

CLRS, fourth edition · Lean 4 formalization

The proofs below use the models and assumptions described in the scope and implementation notes.

Imports

4.1. Multiplying Square Matrices

This section formalizes the naive square-matrix-multiplication algorithm of CLRS §4.1 (fourth edition). On the depth-indexed power-of-two squares CLRS.Chapter04.SqMat it defines the recursive eight-product SQUARE-MATRIX-MULTIPLY-RECURSIVE, proves it computes the ordinary matrix product at every depth, and shows the work recurrence T(n) = 8 T(⌊n/2⌋) + n² is Θ(n³) by Master-theorem case 1.

The representation and Master-theorem infrastructure are shared with §4.2 (Section_04_2_Strassen_Algorithm), where SqMat R k is a 2^k × 2^k block matrix and the recursive runtime analysis discharges the floor/ceiling Master-theorem case 1 wrapper.

The companion MatrixExecution module, exported by the chapter guide, computes both value and scalar work in one recursion. Its MatrixExecution.mulWithCost_work_eq identifies that count with this budget on side lengths 2^k, and MatrixExecution.mulWithCost_theta proves cubic scalar work for arbitrary input families. padOne is only a one-level embedding of a power-of-two square.

Main results:

  • Definition mulRec: the recursive eight-product multiplication on SqMat (CLRS SQUARE-MATRIX-MULTIPLY-RECURSIVE).

  • Theorem mulRec_correct: mulRec R k A B = A * B at every depth.

  • Theorem mulRec_padOne_corner: zero-padding into the next power of two preserves the top-left product.

  • Theorem mul_runtime_bigTheta: the work recurrence T(n) = 8 T(⌊n/2⌋) + n² is Θ(n^(log₂ 8)).

  • Theorem realLogScale_eight_two: the comparison scale at a = 8, b = 2 is the polynomial n³, so the runtime bound is exactly Θ(n³).

Notation conventions used in this section:

  • R : the scalar ring

  • SqMat R k : a 2^k × 2^k square matrix over R

  • mulWork, T : the recursive work/cost function

namespace CLRSnamespace Chapter04

The naive 2 × 2 block product

section Naive2variable {S : Type*} [Ring S]

The naive 2 × 2 block product (the combine step of the recursive algorithm): each output block is the sum of two block products, eight in total. This is the straightforward SQUARE-MATRIX-MULTIPLY block arithmetic that Strassen's seven products optimize away.

def mul2 (M N : Matrix (Fin 2) (Fin 2) S) : Matrix (Fin 2) (Fin 2) S := !![M 0 0 * N 0 0 + M 0 1 * N 1 0, M 0 0 * N 0 1 + M 0 1 * N 1 1; M 1 0 * N 0 0 + M 1 1 * N 1 0, M 1 0 * N 0 1 + M 1 1 * N 1 1]

The naive block product computes the ordinary 2 × 2 matrix product.

theorem mul2_eq_mul (M N : Matrix (Fin 2) (Fin 2) S) : mul2 M N = M * N := by ext i j fin_cases i <;> fin_cases j <;> simp [mul2, Matrix.mul_apply, Fin.sum_univ_two]
end Naive2

Recursive naive multiplication on power-of-two squares

The recursive SQUARE-MATRIX-MULTIPLY-RECURSIVE algorithm. mulRec R 0 is the scalar base case (conventional multiplication); mulRec R (k+1) partitions both factors into four depth-k blocks and forms the eight recursive block products (CLRS §4.1): each output block is the sum of two of them.

def mulRec (R : Type u) [Ring R] : ∀ k, SqMat R k → SqMat R k → SqMat R k | 0, x, y => x * y | (k + 1), A, B => !![mulRec R k (A 0 0) (B 0 0) + mulRec R k (A 0 1) (B 1 0), mulRec R k (A 0 0) (B 0 1) + mulRec R k (A 0 1) (B 1 1); mulRec R k (A 1 0) (B 0 0) + mulRec R k (A 1 1) (B 1 0), mulRec R k (A 1 0) (B 0 1) + mulRec R k (A 1 1) (B 1 1)]

Correctness of the recursive naive multiplication: at every depth it returns the ordinary matrix product A * B. The proof is induction on depth; each step rewrites the eight recursive products by the induction hypothesis and then applies the 2 × 2 identity CLRS.­Chapter04.­mul2_eq_mul.

theorem mulRec_eq_mul (R : Type u) [Ring R] : ∀ (k : ℕ) (A B : SqMat R k), mulRec R k A B = A * B | 0, x, y => rfl | (k + 1), A, B => by have IH : ∀ X Y : SqMat R k, mulRec R k X Y = X * Y := mulRec_eq_mul R k have hstep : mulRec R (k + 1) A B = mul2 A B := by simp only [mulRec, mul2, IH] rw [hstep] exact mul2_eq_mul A B

Reader-facing correctness theorem for the recursive algorithm: on a 2^k × 2^k square, CLRS.­Chapter04.­mulRec produces the true matrix product.

theorem mulRec_correct (R : Type u) [Ring R] (k : ℕ) (A B : SqMat R k) : mulRec R k A B = A * B := mulRec_eq_mul R k A B

Running the recursive naive multiplication on two zero-padded inputs recovers the padded product: padding to the next power of two does not change the meaningful top-left product. This composes CLRS.­Chapter04.­mulRec_correct with CLRS.­Chapter04.­padOne_mul.

theorem mulRec_padOne (R : Type u) [Ring R] (k : ℕ) (x y : SqMat R k) : mulRec R (k + 1) (padOne R k x) (padOne R k y) = padOne R k (x * y) := by rw [mulRec_correct, padOne_mul]

Extracting the corner after a padded naive multiplication returns the original product x * y.

theorem mulRec_padOne_corner (R : Type u) [Ring R] (k : ℕ) (x y : SqMat R k) : (mulRec R (k + 1) (padOne R k x) (padOne R k y)) 0 0 = x * y := by rw [mulRec_padOne] show (!![x * y, 0; 0, 0] : Matrix (Fin 2) (Fin 2) (SqMat R k)) 0 0 = x * y simp

Runtime: T(n) = 8 T(⌊n/2⌋) + n² is Θ(n³)

The naive work recurrence T(n) = 8 T(⌊n/2⌋) + n²: eight recursive subproblems of half size plus quadratic block-combination work, with base value T(0) = 0. This is the CLRS cost recurrence whose solution is the running time of CLRS.­Chapter04.­mulRec.

noncomputable def mulWork : ℕ → ℝ | 0 => 0 | (n + 1) => 8 * mulWork ((n + 1) / 2) + ((n + 1 : ℕ) : ℝ) ^ 2 decreasing_by exact Nat.div_lt_self (Nat.succ_pos n) (by norm_num)

Base value of the work recurrence.

theorem mulWork_zero : mulWork 0 = 0 := by rw [mulWork]

One recursion step of the work recurrence at a successor argument.

theorem mulWork_succ (n : ℕ) : mulWork (n + 1) = 8 * mulWork ((n + 1) / 2) + ((n + 1 : ℕ) : ℝ) ^ 2 := by rw [mulWork]

One recursion step of the work recurrence at any positive argument.

theorem mulWork_pos_step (n : ℕ) (hn : 0 < n) : mulWork n = 8 * mulWork (n / 2) + ((n : ℕ) : ℝ) ^ 2 := by obtain ⟨m, rfl⟩ := Nat.exists_eq_succ_of_ne_zero hn.ne' exact mulWork_succ m

The forcing term f(n) = T(n) - 8 T(⌊n/2⌋) of the recurrence. Choosing f as this defect makes the CLRS floor recurrence T(n) = 8 T(⌊n/2⌋) + f(n) hold definitionally at every input.

noncomputable def mulForcing (n : ℕ) : ℝ := mulWork n - 8 * mulWork (n / 2)

The work function satisfies the Chapter 4 floor-division Master recurrence with a = 8, b = 2.

theorem mulWork_floorRec : FloorDivideRecurrence 8 2 mulForcing mulWork := by refine ⟨fun n => ?_⟩ simp only [mulForcing] push_cast ring

The work function is nonnegative.

theorem mulWork_nonneg : ∀ n, 0 ≤ mulWork n := by intro n induction n using Nat.strong_induction_on with | _ n ih => rcases Nat.eq_zero_or_pos n with hn | hn · subst hn; simp [mulWork_zero] · rw [mulWork_pos_step n hn] have hlt : n / 2 < n := Nat.div_lt_self hn (by norm_num) have hrec := ih (n / 2) hlt nlinarith [hrec, sq_nonneg ((n : ℕ) : ℝ)]

The work function is nondecreasing across one step.

theorem mulWork_le_succ : ∀ n, mulWork n ≤ mulWork (n + 1) := by intro n induction n using Nat.strong_induction_on with | _ n ih => rcases Nat.eq_zero_or_pos n with hn | hn · subst hn; rw [mulWork_zero]; exact mulWork_nonneg _ · rw [mulWork_pos_step n hn, mulWork_pos_step (n + 1) (Nat.succ_pos n)] have hcast : ((n : ℕ) : ℝ) ^ 2 ≤ ((n + 1 : ℕ) : ℝ) ^ 2 := by have h1 : ((n : ℕ) : ℝ) ≤ ((n + 1 : ℕ) : ℝ) := by push_cast; linarith have hn_nonneg : (0 : ℝ) ≤ (n : ℕ) := Nat.cast_nonneg _ nlinarith [hn_nonneg, h1] rcases (by omega : (n + 1) / 2 = n / 2 ∨ (n + 1) / 2 = n / 2 + 1) with h | h · rw [h]; linarith [hcast] · rw [h] have hj : n / 2 < n := Nat.div_lt_self hn (by norm_num) have hstep := ih (n / 2) hj linarith [hstep, hcast]

The work function is monotone.

theorem mulWork_monotone : Monotone mulWork := monotone_nat_of_le_succ mulWork_le_succ

The work function satisfies the absolute-value monotonicity interface.

theorem mulWork_monotoneAbs : MonotoneAbs mulWork := by intro m n hmn rw [abs_of_nonneg (mulWork_nonneg m), abs_of_nonneg (mulWork_nonneg n)] exact mulWork_monotone hmn

The normalized forcing on exact powers is the convergent geometric sequence (1/2)^(k+1): on n = 2^(k+1) the forcing is exactly the block-work (2^(k+1))² = 4^(k+1), so dividing by 8^(k+1) gives (4/8)^(k+1) = (1/2)^(k+1). This is what places the naive recurrence in Master case 1 (the forcing n² is polynomially smaller than the critical n^(log₂ 8) = n³).

theorem mul_normForcing (k : ℕ) : normalizedForcing 8 2 mulForcing k = (1 / 2 : ℝ) ^ (k + 1) := by have hpos : 0 < 2 ^ (k + 1) := pow_pos (by norm_num) _ have hdiv : 2 ^ (k + 1) / 2 = 2 ^ k := by rw [pow_succ]; omega have hcast : ((2 ^ (k + 1) : ℕ) : ℝ) ^ 2 = (4 : ℝ) ^ (k + 1) := by push_cast rw [show (4 : ℝ) = 2 ^ 2 by norm_num, ← pow_mul, ← pow_mul, Nat.mul_comm 2 (k + 1)] have hforcing : mulForcing (2 ^ (k + 1)) = (4 : ℝ) ^ (k + 1) := by unfold mulForcing rw [mulWork_pos_step (2 ^ (k + 1)) hpos, hdiv] linarith [hcast] unfold normalizedForcing rw [hforcing, ← div_pow] norm_num

Case-1 hypothesis: the normalized forcing is nonnegative.

theorem mul_term_nonneg (k : ℕ) : 0 ≤ normalizedForcing 8 2 mulForcing k := by rw [mul_normForcing]; positivity

Case-1 hypothesis: the normalized forcing is bounded by a geometric sequence.

theorem mul_term_upper (k : ℕ) : normalizedForcing 8 2 mulForcing k ≤ (1 / 2 : ℝ) * (1 / 2 : ℝ) ^ k := by rw [mul_normForcing, pow_succ] exact le_of_eq (mul_comm _ _)

Value of the work recurrence at the base input 1.

theorem mulWork_one : mulWork 1 = 1 := by rw [show (1 : ℕ) = 0 + 1 from rfl, mulWork_succ] norm_num [mulWork_zero]

Positivity of the normalized base value, a case-1 hypothesis.

theorem mul_base_pos : 0 < normalizedValue 8 2 mulWork 0 := by unfold normalizedValue norm_num [mulWork_one]

Runtime of the naive matrix-multiplication algorithm. The recurrence T(n) = 8 T(⌊n/2⌋) + n² is Θ(n^(log₂ 8)). This is the CLRS Θ(n³) bound, obtained by discharging Master-theorem case 1 (the forcing n² is polynomially smaller than the critical n^(log₂ 8)) through the Chapter 4 wrapper CLRS.­Chapter04.­floorDivide_allInput_masterCase1_realLogScale.

theorem mul_runtime_bigTheta : Chapter03.isBigTheta mulWork (realLogScale 8 2) := floorDivide_allInput_masterCase1_realLogScale 8 2 mulForcing mulWork mulWork_floorRec (by norm_num) (by norm_num) mulWork_monotoneAbs mul_base_pos mul_term_nonneg (r := 1 / 2) (C := 1 / 2) (by norm_num) (by norm_num) (by norm_num) mul_term_upper

The comparison scale CLRS.­Chapter04.­realLogScale at a = 8, b = 2 is the polynomial n³, since log₂ 8 = 3. So CLRS.­Chapter04.­mul_runtime_bigTheta is exactly the CLRS Θ(n³) statement.

Try this: [apply] ring_nf The `ring` tactic failed to close the goal. Use `ring_nf` to obtain a normal form. Note that `ring` works primarily in *commutative* rings. If you have a noncommutative ring, abelian group or module, consider using `noncomm_ring`, `abel` or `module` instead. theorem realLogScale_eight_two (n : ℕ) : realLogScale 8 2 n = (n : ℝ) ^ 3 := by rw [realLogScale, realLogExponent] have h : Real.log (((8 : ℕ) : ℝ)) / Real.log (((2 : ℕ) : ℝ)) = 3 := by rw [show (((8 : ℕ) : ℝ)) = (2 : ℝ) ^ 3 by norm_num, Real.log_pow] have hlog : Real.log (2 : ℝ) ≠ 0 := by exact Real.log_ne_zero.mpr ⟨by norm_num, by norm_num, by norm_num⟩ field_simp [hlog] Try this: [apply] ring_nf The `ring` tactic failed to close the goal. Use `ring_nf` to obtain a normal form. Note that `ring` works primarily in *commutative* rings. If you have a noncommutative ring, abelian group or module, consider using `noncomm_ring`, `abel` or `module` instead.ring rw [h] simp
end Chapter04end CLRS

Definitions and proofs

CLRSLean.FourthEdition.Chapter_04.MatrixExecution.Asymptotics

Asymptotic scalar work on power-of-two squares

The functions below read counters from arbitrary families of input matrices. The asymptotic variable is depth k; the comparison functions use their actual side length 2^k. Consequently these statements do not extend the input type to all natural dimensions.

namespace CLRS.Chapter04.MatrixExecution private theorem cubic_side (k : Nat) : ((2 ^ k : Nat) : ℝ) ^ 3 = (8 : ℝ) ^ k := by rw [Nat.cast_pow, Nat.cast_ofNat, ← pow_mul, Nat.mul_comm k 3, pow_mul] norm_num private theorem strassen_side (k : Nat) : ((2 ^ k : Nat) : ℝ) ^ Real.logb 2 7 = (7 : ℝ) ^ k := by rw [Nat.cast_pow, Nat.cast_ofNat, ← Real.rpow_pow_comm (by norm_num)] rw [Real.rpow_logb (by norm_num) (by norm_num) (by norm_num)]

Every input family has cubic scalar work in its power-of-two side length.

theorem mulWithCost_theta (R : Type u) [Ring R] (A B : ∀ k, SqMat R k) : Chapter03.isBigTheta (fun k => ((mulWithCost R k (A k) (B k)).work : ℝ)) (fun k => ((2 ^ k : Nat) : ℝ) ^ 3) := by constructor · rw [Chapter03.isBigO_iff] refine ⟨2, by norm_num, 0, ?_⟩ intro k _ rw [mulWithCost_work, cubic_side, abs_of_nonneg (by positivity), abs_of_nonneg (by positivity)] exact_mod_cast (mulCount_bounds k).2 · rw [Chapter03.isBigOmega_iff] refine ⟨1, by norm_num, 0, ?_⟩ intro k _ rw [mulWithCost_work, cubic_side, abs_of_nonneg (by positivity), abs_of_nonneg (by positivity), one_mul] exact_mod_cast (mulCount_bounds k).1

Every input family has Strassen's scalar-work exponent on its side length.

theorem strassenWithCost_theta (R : Type u) [Ring R] (A B : ∀ k, SqMat R k) : Chapter03.isBigTheta (fun k => ((strassenWithCost R k (A k) (B k)).work : ℝ)) (fun k => ((2 ^ k : Nat) : ℝ) ^ Real.logb 2 7) := by constructor · rw [Chapter03.isBigO_iff] refine ⟨7, by norm_num, 0, ?_⟩ intro k _ rw [strassenWithCost_work, strassen_side, abs_of_nonneg (by positivity), abs_of_nonneg (by positivity)] exact_mod_cast (strassenCount_bounds k).2 · rw [Chapter03.isBigOmega_iff] refine ⟨1, by norm_num, 0, ?_⟩ intro k _ rw [strassenWithCost_work, strassen_side, abs_of_nonneg (by positivity), abs_of_nonneg (by positivity), one_mul] exact_mod_cast (strassenCount_bounds k).1
end CLRS.Chapter04.MatrixExecution

CLRSLean.FourthEdition.Chapter_04.MatrixExecution.Algorithms

Matrix multiplication with scalar work

Both algorithms obtain their value and work from the same recursion. Every matrix sum/difference is itself evaluated by the counted leaf traversal. Strassen's seven product results are shared; each result is computed and charged once, even when it contributes to more than one output block.

namespace CLRS.Chapter04.MatrixExecution

Eight recursive products and four counted output-block sums.

def mulWithCost (R : Type u) [Ring R] : ∀ k, SqMat R k → SqMat R k → Result R k | 0, x, y => ⟨x * y, 1⟩ | k + 1, A, B => let p1 := mulWithCost R k (A 0 0) (B 0 0) let p2 := mulWithCost R k (A 0 1) (B 1 0) let p3 := mulWithCost R k (A 0 0) (B 0 1) let p4 := mulWithCost R k (A 0 1) (B 1 1) let p5 := mulWithCost R k (A 1 0) (B 0 0) let p6 := mulWithCost R k (A 1 1) (B 1 0) let p7 := mulWithCost R k (A 1 0) (B 0 1) let p8 := mulWithCost R k (A 1 1) (B 1 1) let c11 := zipWithCost R (· + ·) k p1.value p2.value let c12 := zipWithCost R (· + ·) k p3.value p4.value let c21 := zipWithCost R (· + ·) k p5.value p6.value let c22 := zipWithCost R (· + ·) k p7.value p8.value ⟨!![c11.value, c12.value; c21.value, c22.value], p1.work + p2.work + p3.work + p4.work + p5.work + p6.work + p7.work + p8.work + c11.work + c12.work + c21.work + c22.work⟩

Strassen with all ten preparation and eight reassembly sums/differences counted.

def strassenWithCost (R : Type u) [Ring R] : ∀ k, SqMat R k → SqMat R k → Result R k | 0, x, y => ⟨x * y, 1⟩ | k + 1, A, B => let t1 := zipWithCost R (· - ·) k (B 0 1) (B 1 1) let t2 := zipWithCost R (· + ·) k (A 0 0) (A 0 1) let t3 := zipWithCost R (· + ·) k (A 1 0) (A 1 1) let t4 := zipWithCost R (· - ·) k (B 1 0) (B 0 0) let t5 := zipWithCost R (· + ·) k (A 0 0) (A 1 1) let t6 := zipWithCost R (· + ·) k (B 0 0) (B 1 1) let t7 := zipWithCost R (· - ·) k (A 0 1) (A 1 1) let t8 := zipWithCost R (· + ·) k (B 1 0) (B 1 1) let t9 := zipWithCost R (· - ·) k (A 0 0) (A 1 0) let t10 := zipWithCost R (· + ·) k (B 0 0) (B 0 1) let p1 := strassenWithCost R k (A 0 0) t1.value let p2 := strassenWithCost R k t2.value (B 1 1) let p3 := strassenWithCost R k t3.value (B 0 0) let p4 := strassenWithCost R k (A 1 1) t4.value let p5 := strassenWithCost R k t5.value t6.value let p6 := strassenWithCost R k t7.value t8.value let p7 := strassenWithCost R k t9.value t10.value let c11a := zipWithCost R (· + ·) k p5.value p4.value let c11b := zipWithCost R (· - ·) k c11a.value p2.value let c11 := zipWithCost R (· + ·) k c11b.value p6.value let c12 := zipWithCost R (· + ·) k p1.value p2.value let c21 := zipWithCost R (· + ·) k p3.value p4.value let c22a := zipWithCost R (· + ·) k p5.value p1.value let c22b := zipWithCost R (· - ·) k c22a.value p3.value let c22 := zipWithCost R (· - ·) k c22b.value p7.value ⟨!![c11.value, c12.value; c21.value, c22.value], t1.work + t2.work + t3.work + t4.work + t5.work + t6.work + t7.work + t8.work + t9.work + t10.work + p1.work + p2.work + p3.work + p4.work + p5.work + p6.work + p7.work + c11a.work + c11b.work + c11.work + c12.work + c21.work + c22a.work + c22b.work + c22.work⟩

Erasing work recovers the existing eight-product algorithm.

theorem mulWithCost_value (R : Type u) [Ring R] : ∀ (k : Nat) (A B : SqMat R k), (mulWithCost R k A B).value = mulRec R k A B := by intro k induction k with | zero => intros; rfl | succ k ih => intro A B simp only [mulWithCost, addWithCost_value, ih, mulRec]

Erasing work recovers the existing seven-product algorithm.

theorem strassenWithCost_value (R : Type u) [Ring R] : ∀ (k : Nat) (A B : SqMat R k), (strassenWithCost R k A B).value = strassenRec R k A B := by intro k induction k with | zero => intros; rfl | succ k ih => intro A B simp only [strassenWithCost, addWithCost_value, subWithCost_value, ih, strassenRec]

Depth recurrence proved below to be the eight-product execution's work.

def mulCount : Nat → Nat | 0 => 1 | k + 1 => 8 * mulCount k + 4 * 4 ^ k

Depth recurrence including Strassen's eighteen block sums/differences.

def strassenCount : Nat → Nat | 0 => 1 | k + 1 => 7 * strassenCount k + 18 * 4 ^ k
theorem mulWithCost_work (R : Type u) [Ring R] : ∀ (k : Nat) (A B : SqMat R k), (mulWithCost R k A B).work = mulCount k := by intro k induction k with | zero => intros; rfl | succ k ih => intro A B simp only [mulWithCost, zipWithCost_work, ih, mulCount] omegatheorem strassenWithCost_work (R : Type u) [Ring R] : ∀ (k : Nat) (A B : SqMat R k), (strassenWithCost R k A B).work = strassenCount k := by intro k induction k with | zero => intros; rfl | succ k ih => intro A B simp only [strassenWithCost, zipWithCost_work, ih, strassenCount] omegaend CLRS.Chapter04.MatrixExecution

CLRSLean.FourthEdition.Chapter_04.MatrixExecution.Basic

Scalar-operation execution for block matrices

The counter charges one scalar addition, subtraction, or multiplication. Matrix addition/subtraction visits all four child blocks recursively; it is not charged as one scalar operation. Indexing, immutable representation, allocation, and forming the four output blocks are outside this arithmetic model. Local results are shared when a later expression uses them more than once.

namespace CLRS.Chapter04.MatrixExecution

A returned matrix and the scalar arithmetic operations used to obtain it.

structure Result (R : Type u) (k : Nat) where value : SqMat R k work : Nat

Apply one scalar operation at every corresponding pair of leaves.

def zipWithCost (R : Type u) (op : R → R → R) : ∀ k, SqMat R k → SqMat R k → Result R k | 0, x, y => ⟨op x y, 1⟩ | k + 1, A, B => let a := zipWithCost R op k (A 0 0) (B 0 0) let b := zipWithCost R op k (A 0 1) (B 0 1) let c := zipWithCost R op k (A 1 0) (B 1 0) let d := zipWithCost R op k (A 1 1) (B 1 1) ⟨!![a.value, b.value; c.value, d.value], a.work + b.work + c.work + d.work⟩

The count comes from visiting the scalar leaves of the returned matrix.

theorem zipWithCost_work (R : Type u) (op : R → R → R) : ∀ (k : Nat) (A B : SqMat R k), (zipWithCost R op k A B).work = 4 ^ k := by intro k induction k with | zero => intros; rfl | succ k ih => intro A B simp only [zipWithCost, ih, pow_succ] omega
theorem addWithCost_value (R : Type u) [Ring R] : ∀ (k : Nat) (A B : SqMat R k), (zipWithCost R (· + ·) k A B).value = A + B := by intro k induction k with | zero => intros; rfl | succ k ih => intro A B funext i j fin_cases i <;> fin_cases j <;> simp [zipWithCost, ih] <;> rfltheorem subWithCost_value (R : Type u) [Ring R] : ∀ (k : Nat) (A B : SqMat R k), (zipWithCost R (· - ·) k A B).value = A - B := by intro k induction k with | zero => intros; rfl | succ k ih => intro A B funext i j fin_cases i <;> fin_cases j <;> simp [zipWithCost, ih] <;> rflend CLRS.Chapter04.MatrixExecution

CLRSLean.FourthEdition.Chapter_04.MatrixExecution.Bounds

Bounds for executed scalar arithmetic

These results concern matrices of side length 2^k. The eight-product counter equals the existing work recurrence on that domain. Strassen's eighteen block sums give a different exact count, bounded by constant multiples of the existing recurrence. Neither result claims an arbitrary-dimension padding API or a machine-time bound for ring operations of nonconstant cost.

namespace CLRS.Chapter04.MatrixExecution private theorem side_square (k : Nat) : ((2 ^ k : Nat) : ℝ) ^ 2 = (4 ^ k : Nat) := by norm_cast rw [← pow_mul, Nat.mul_comm k 2, pow_mul] norm_num theorem mulCount_eq_budget (k : Nat) : (mulCount k : ℝ) = mulWork (2 ^ k) := by induction k with | zero => simp [mulCount, mulWork_one] | succ k ih => rw [mulCount, Nat.cast_add, Nat.cast_mul, ih, mulWork_pos_step (2 ^ (k + 1)) (by positivity)] have hdiv : 2 ^ (k + 1) / 2 = 2 ^ k := by rw [pow_succ]; omega rw [hdiv, side_square] simp only [pow_succ 4 k, Nat.cast_mul, Nat.cast_ofNat] norm_num ring theorem strassenCount_budget_bounds (k : Nat) : strassenWork (2 ^ k) ≤ (strassenCount k : ℝ) ∧ (strassenCount k : ℝ) ≤ 5 * strassenWork (2 ^ k) := by induction k with | zero => norm_num [strassenCount, strassenWork_one] | succ k ih => rw [strassenCount, Nat.cast_add, Nat.cast_mul, strassenWork_pos_step (2 ^ (k + 1)) (by positivity)] have hdiv : 2 ^ (k + 1) / 2 = 2 ^ k := by rw [pow_succ]; omega rw [hdiv, side_square] simp only [pow_succ 4 k, Nat.cast_mul, Nat.cast_ofNat] have hnonneg : (0 : ℝ) ≤ (4 ^ k : Nat) := by positivity constructor <;> nlinarith [ih.1, ih.2]

Equality with the budget is proved from the returned execution counter.

theorem mulWithCost_work_eq (R : Type u) [Ring R] (k : Nat) (A B : SqMat R k) : ((mulWithCost R k A B).work : ℝ) = mulWork (2 ^ k) := by rw [mulWithCost_work, mulCount_eq_budget]

Strassen's actual arithmetic count is within a factor of five of the budget.

theorem strassenWithCost_work_bounds (R : Type u) [Ring R] (k : Nat) (A B : SqMat R k) : strassenWork (2 ^ k) ≤ ((strassenWithCost R k A B).work : ℝ) ∧ ((strassenWithCost R k A B).work : ℝ) ≤ 5 * strassenWork (2 ^ k) := by rw [strassenWithCost_work] exact strassenCount_budget_bounds k
theorem mulCount_closed (k : Nat) : mulCount k + 4 ^ k = 2 * 8 ^ k := by induction k with | zero => norm_num [mulCount] | succ k ih => simp only [mulCount, pow_succ]; omegatheorem strassenCount_closed (k : Nat) : strassenCount k + 6 * 4 ^ k = 7 * 7 ^ k := by induction k with | zero => norm_num [strassenCount] | succ k ih => simp only [strassenCount, pow_succ]; omega theorem mulCount_bounds (k : Nat) : 8 ^ k ≤ mulCount k ∧ mulCount k ≤ 2 * 8 ^ k := by have h := mulCount_closed k have hpow : 4 ^ k ≤ 8 ^ k := Nat.pow_le_pow_left (by norm_num) k have hnonneg := Nat.zero_le (4 ^ k) omega theorem strassenCount_bounds (k : Nat) : 7 ^ k ≤ strassenCount k ∧ strassenCount k ≤ 7 * 7 ^ k := by have h := strassenCount_closed k have hpow : 4 ^ k ≤ 7 ^ k := Nat.pow_le_pow_left (by norm_num) k omega

Value correctness and work for the same eight-product run.

theorem mulWithCost_correct (R : Type u) [Ring R] (k : Nat) (A B : SqMat R k) : (mulWithCost R k A B).value = A * B ∧ ((mulWithCost R k A B).work : ℝ) = mulWork (2 ^ k) := ⟨by rw [mulWithCost_value, mulRec_correct], mulWithCost_work_eq R k A B⟩

Value correctness and work bounds for the same seven-product run.

theorem strassenWithCost_correct (R : Type u) [Ring R] (k : Nat) (A B : SqMat R k) : (strassenWithCost R k A B).value = A * B ∧ strassenWork (2 ^ k) ≤ ((strassenWithCost R k A B).work : ℝ) ∧ ((strassenWithCost R k A B).work : ℝ) ≤ 5 * strassenWork (2 ^ k) := ⟨by rw [strassenWithCost_value, strassenRec_correct], strassenWithCost_work_bounds R k A B⟩
end CLRS.Chapter04.MatrixExecution

CLRSLean.FourthEdition.Chapter_04.Section_04_2_Strassen_Algorithm

Zero-padded factors multiply block-diagonally: the top-left corner of the product of two padded matrices is the product of the two originals, with the rest still zero.

theorem padOne_mul (R : Type u) [Ring R] (k : ℕ) (x y : SqMat R k) : padOne R k x * padOne R k y = padOne R k (x * y) := by show (!![x, 0; 0, 0] : Matrix (Fin 2) (Fin 2) (SqMat R k)) * !![y, 0; 0, 0] = !![x * y, 0; 0, 0] ext i j fin_cases i <;> fin_cases j <;> simp [Matrix.mul_apply, Fin.sum_univ_two]

CLRSLean.Chapter_04.Section_04_6_Master_Theorem_All_Input

The real-log comparison scale n^(log_b a). This is the textbook scale used in the standard CLRS statement of the Master theorem: the homogeneous-solution growth rate without floors and ceilings.

For integer exponents it coincides with the ordinary polynomial scale polynomialScale.

noncomputable def realLogScale (a b : ℕ) (n : ℕ) : ℝ := (n : ℝ) ^ (realLogExponent a b)

Floor-division all-input Master case 1 stated in the textbook real-log scale n^(log_b a).

theorem floorDivide_allInput_masterCase1_realLogScale (a b : ℕ) (f T : ℕ → ℝ) (h_rec : FloorDivideRecurrence a b f T) (ha : 1 ≤ a) (hb : 1 < b) (hT_mono : MonotoneAbs T) (h_base_pos : 0 < normalizedValue a b T 0) (h_term_nonneg : ∀ k, 0 ≤ normalizedForcing a b f k) {r C : ℝ} (hr_nonneg : 0 ≤ r) (hr_lt_one : r < 1) (hC_pos : 0 < C) (h_term_upper : ∀ k, normalizedForcing a b f k ≤ C * r ^ k) : Chapter03.isBigTheta T (realLogScale a b) := by exact Chapter03.isBigTheta_trans (floorDivide_allInput_masterCase1_criticalPowerScale a b f T h_rec ha hb hT_mono h_base_pos h_term_nonneg hr_nonneg hr_lt_one hC_pos h_term_upper) (criticalPowerScale_isBigTheta_realLogScale a b ha hb)
Imports

4.2. Strassen’s Algorithm for Matrix Multiplication

This file formalizes Strassen's algorithm, from its algebraic 2 by 2 core all the way to a recursive algorithm on power-of-two squares with a Θ(n^(log₂ 7)) runtime bound.

A Matrix2 R should be read as a 2 by 2 block matrix whose entries live in an arbitrary ring. The theorem strassen2x2_correct proves that Strassen's seven block products reconstruct ordinary 2 by 2 block matrix multiplication.

Recursive refinement

The type CLRS.Chapter04.SqMat is a depth-indexed square matrix: SqMat R 0 = R and SqMat R (k+1) = Matrix (Fin 2) (Fin 2) (SqMat R k), so SqMat R k is a genuine 2^k × 2^k matrix ring built by nesting the 2 by 2 block structure. CLRS.Chapter04.strassenRec is the recursive seven-multiplication algorithm: it bottoms out at the scalar base case SqMat R 0 = R and otherwise combines the seven Strassen products of its four sub-blocks. CLRS.Chapter04.strassenRec_correct proves it computes the ordinary matrix product A * B at every depth, and CLRS.Chapter04.strassenRec_padOne shows that zero-padding a matrix into the next power-of-two block preserves the product in the top-left corner.

Runtime

The work recurrence CLRS.Chapter04.strassenWork satisfies the CLRS floor recurrence T(n) = 7 T(⌊n/2⌋) + n², i.e. seven recursive products plus quadratic block-addition work. Feeding this into the Chapter 4 Master theorem case-1 wrapper CLRS.Chapter04.floorDivide_allInput_masterCase1_realLogScale gives CLRS.Chapter04.strassen_runtime_bigTheta: T = Θ(n^(log₂ 7)), the textbook Θ(n^(lg 7)) bound.

The companion MatrixExecution module, exported by the chapter guide, counts all ten preparation and eight reassembly block sums/differences by visiting their scalar entries. MatrixExecution.strassenWithCost_work_bounds places this actual count between one and five times the budget on dimensions 2^k; MatrixExecution.strassenWithCost_theta proves its exponent. The budget is not an exact scalar count. Padding here is a one-level embedding of an already power-of-two square, not an arbitrary-dimension interface.

Main results:

  • Theorem strassen2x2_correct: the 2 by 2 block algebra core.

  • Theorem strassenRec_correct: the recursive algorithm computes A * B.

  • Theorem strassenRec_padOne: zero-padding preserves the corner product.

  • Theorem strassen_runtime_bigTheta: T(n) = Θ(n^(log₂ 7)).

Notation conventions used in this section:

  • R : the scalar ring

  • SqMat R k : a 2^k × 2^k square matrix over R

  • T, strassenWork : the recursive work/cost function

namespace CLRSnamespace Chapter04

A 2 by 2 block matrix.

structure Matrix2 (R : Type*) where a11 : R a12 : R a21 : R a22 : R
namespace Matrix2@[ext] theorem ext {R : Type*} {A B : Matrix2 R} (h11 : A.a11 = B.a11) (h12 : A.a12 = B.a12) (h21 : A.a21 = B.a21) (h22 : A.a22 = B.a22) : A = B := by cases A cases B simp_allvariable {R : Type*} [Ring R]

Ordinary 2 by 2 block matrix multiplication.

def mul (A B : Matrix2 R) : Matrix2 R := { a11 := A.a11 * B.a11 + A.a12 * B.a21 a12 := A.a11 * B.a12 + A.a12 * B.a22 a21 := A.a21 * B.a11 + A.a22 * B.a21 a22 := A.a21 * B.a12 + A.a22 * B.a22 }

Strassen's seven-product reconstruction for 2 by 2 block matrices.

def strassen (A B : Matrix2 R) : Matrix2 R := let p1 := A.a11 * (B.a12 - B.a22) let p2 := (A.a11 + A.a12) * B.a22 let p3 := (A.a21 + A.a22) * B.a11 let p4 := A.a22 * (B.a21 - B.a11) let p5 := (A.a11 + A.a22) * (B.a11 + B.a22) let p6 := (A.a12 - A.a22) * (B.a21 + B.a22) let p7 := (A.a11 - A.a21) * (B.a11 + B.a12) { a11 := p5 + p4 - p2 + p6 a12 := p1 + p2 a21 := p3 + p4 a22 := p5 + p1 - p3 - p7 }

Strassen's seven products compute the ordinary 2 by 2 block product.

theorem strassen_eq_mul (A B : Matrix2 R) : strassen A B = mul A B := by ext <;> simp [strassen, mul] <;> noncomm_ring
end Matrix2

Reader-facing correctness theorem for CLRS Section 4.2: the algebraic Strassen reconstruction is extensionally equal to ordinary 2 by 2 block matrix multiplication.

theorem strassen2x2_correct {R : Type*} [Ring R] (A B : Matrix2 R) : Matrix2.strassen A B = Matrix2.mul A B := Matrix2.strassen_eq_mul A B

Strassen's seven products on Matrix (Fin 2) (Fin 2)

section StrassenMatrixvariable {S : Type*} [Ring S]

Strassen's seven-product reconstruction expressed directly on Matrix (Fin 2) (Fin 2) S. This is the Matrix-valued restatement of the block algebra CLRS.­Chapter04.­Matrix2.­strassen, and it is the shape used by the recursive algorithm below. Each p is one of the seven products P₁…P₇ from CLRS.

def strassen2 (M N : Matrix (Fin 2) (Fin 2) S) : Matrix (Fin 2) (Fin 2) S := let p1 := M 0 0 * (N 0 1 - N 1 1) let p2 := (M 0 0 + M 0 1) * N 1 1 let p3 := (M 1 0 + M 1 1) * N 0 0 let p4 := M 1 1 * (N 1 0 - N 0 0) let p5 := (M 0 0 + M 1 1) * (N 0 0 + N 1 1) let p6 := (M 0 1 - M 1 1) * (N 1 0 + N 1 1) let p7 := (M 0 0 - M 1 0) * (N 0 0 + N 0 1) !![p5 + p4 - p2 + p6, p1 + p2; p3 + p4, p5 + p1 - p3 - p7]

The seven Strassen products compute the ordinary 2 × 2 matrix product. This is the Matrix-valued counterpart of CLRS.­Chapter04.­Matrix2.­strassen_eq_mul.

theorem strassen2_eq_mul (M N : Matrix (Fin 2) (Fin 2) S) : strassen2 M N = M * N := by ext i j fin_cases i <;> fin_cases j <;> simp [strassen2, Matrix.mul_apply, Fin.sum_univ_two] <;> noncomm_ring
end StrassenMatrix

Recursive Strassen on power-of-two squares

Depth-indexed square matrix over R. SqMat R 0 is the scalar type R, and SqMat R (k+1) is a 2 × 2 block matrix whose entries are depth-k squares. Thus SqMat R k is a 2^k × 2^k matrix realized as a balanced quad-tree of 2 × 2 blocks.

def SqMat (R : Type u) : ℕ → Type u | 0 => R | (k + 1) => Matrix (Fin 2) (Fin 2) (SqMat R k)

The ring structure on CLRS.­Chapter04.­SqMat. At depth 0 it is the scalar ring R; at depth k+1 it is the standard 2 × 2 matrix ring over the depth-k ring, so ordinary multiplication on SqMat R k is exactly block matrix multiplication.

instance instRingSqMat (R : Type u) [Ring R] : ∀ k, Ring (SqMat R k) | 0 => inferInstanceAs (Ring R) | (k + 1) => letI := instRingSqMat R k inferInstanceAs (Ring (Matrix (Fin 2) (Fin 2) (SqMat R k)))

The recursive Strassen algorithm. strassenRec R 0 is the scalar base case (conventional multiplication); strassenRec R (k+1) forms the seven Strassen products of the four sub-blocks with seven recursive calls and reassembles the four output blocks (CLRS STRASSEN).

def strassenRec (R : Type u) [Ring R] : ∀ k, SqMat R k → SqMat R k → SqMat R k | 0, x, y => x * y | (k + 1), A, B => let p1 := strassenRec R k (A 0 0) (B 0 1 - B 1 1) let p2 := strassenRec R k (A 0 0 + A 0 1) (B 1 1) let p3 := strassenRec R k (A 1 0 + A 1 1) (B 0 0) let p4 := strassenRec R k (A 1 1) (B 1 0 - B 0 0) let p5 := strassenRec R k (A 0 0 + A 1 1) (B 0 0 + B 1 1) let p6 := strassenRec R k (A 0 1 - A 1 1) (B 1 0 + B 1 1) let p7 := strassenRec R k (A 0 0 - A 1 0) (B 0 0 + B 0 1) !![p5 + p4 - p2 + p6, p1 + p2; p3 + p4, p5 + p1 - p3 - p7]

Correctness of the recursive Strassen algorithm: at every depth it returns the ordinary matrix product A * B. The proof is induction on depth; each step rewrites the seven recursive products by the induction hypothesis and then applies the 2 by 2 identity CLRS.­Chapter04.­strassen2_eq_mul.

theorem strassenRec_eq_mul (R : Type u) [Ring R] : ∀ (k : ℕ) (A B : SqMat R k), strassenRec R k A B = A * B | 0, x, y => rfl | (k + 1), A, B => by have IH : ∀ X Y : SqMat R k, strassenRec R k X Y = X * Y := strassenRec_eq_mul R k have hstep : strassenRec R (k + 1) A B = strassen2 A B := by simp only [strassenRec, strassen2, IH] rw [hstep] exact strassen2_eq_mul A B

Reader-facing correctness theorem for the recursive algorithm: on a 2^k × 2^k square, CLRS.­Chapter04.­strassenRec produces the true matrix product.

theorem strassenRec_correct (R : Type u) [Ring R] (k : ℕ) (A B : SqMat R k) : strassenRec R k A B = A * B := strassenRec_eq_mul R k A B

One-level zero-padding of power-of-two squares

Zero-padding: embed a depth-k square into the top-left block of a depth-(k+1) square, filling the other three blocks with zeros. This is the padding step of CLRS STRASSEN, which enlarges an n × n input to the next power of two.

def padOne (R : Type u) [Ring R] (k : ℕ) (x : SqMat R k) : SqMat R (k + 1) := !![x, 0; 0, 0]

Zero-padded factors multiply block-diagonally: the top-left corner of the product of two padded matrices is the product of the two originals, with the rest still zero.

theorem padOne_mul (R : Type u) [Ring R] (k : ℕ) (x y : SqMat R k) : padOne R k x * padOne R k y = padOne R k (x * y) := by show (!![x, 0; 0, 0] : Matrix (Fin 2) (Fin 2) (SqMat R k)) * !![y, 0; 0, 0] = !![x * y, 0; 0, 0] ext i j fin_cases i <;> fin_cases j <;> simp [Matrix.mul_apply, Fin.sum_univ_two]

Running the recursive Strassen algorithm on two zero-padded inputs recovers the padded product: padding to the next power of two does not change the meaningful top-left product. This composes CLRS.­Chapter04.­strassenRec_correct with CLRS.­Chapter04.­padOne_mul.

theorem strassenRec_padOne (R : Type u) [Ring R] (k : ℕ) (x y : SqMat R k) : strassenRec R (k + 1) (padOne R k x) (padOne R k y) = padOne R k (x * y) := by rw [strassenRec_correct, padOne_mul]

The top-left block projection, inverse to CLRS.­Chapter04.­padOne on the padded corner. Extracting the corner after a padded Strassen multiplication returns the original product x * y.

theorem strassenRec_padOne_corner (R : Type u) [Ring R] (k : ℕ) (x y : SqMat R k) : (strassenRec R (k + 1) (padOne R k x) (padOne R k y)) 0 0 = x * y := by rw [strassenRec_padOne] show (!![x * y, 0; 0, 0] : Matrix (Fin 2) (Fin 2) (SqMat R k)) 0 0 = x * y simp

Runtime: T(n) = 7 T(⌊n/2⌋) + n² is Θ(n^(log₂ 7))

The Strassen work recurrence T(n) = 7 T(⌊n/2⌋) + n²: seven recursive subproblems of half size plus quadratic block-combination work, with base value T(0) = 0. This is the CLRS cost recurrence whose solution is the running time of CLRS.­Chapter04.­strassenRec.

noncomputable def strassenWork : ℕ → ℝ | 0 => 0 | (n + 1) => 7 * strassenWork ((n + 1) / 2) + ((n + 1 : ℕ) : ℝ) ^ 2 decreasing_by exact Nat.div_lt_self (Nat.succ_pos n) (by norm_num)

Base value of the work recurrence.

theorem strassenWork_zero : strassenWork 0 = 0 := by rw [strassenWork]

One recursion step of the work recurrence at a successor argument.

theorem strassenWork_succ (n : ℕ) : strassenWork (n + 1) = 7 * strassenWork ((n + 1) / 2) + ((n + 1 : ℕ) : ℝ) ^ 2 := by rw [strassenWork]

One recursion step of the work recurrence at any positive argument.

theorem strassenWork_pos_step (n : ℕ) (hn : 0 < n) : strassenWork n = 7 * strassenWork (n / 2) + ((n : ℕ) : ℝ) ^ 2 := by obtain ⟨m, rfl⟩ := Nat.exists_eq_succ_of_ne_zero hn.ne' exact strassenWork_succ m

The forcing term f(n) = T(n) - 7 T(⌊n/2⌋) of the recurrence. Choosing f as this defect makes the CLRS floor recurrence T(n) = 7 T(⌊n/2⌋) + f(n) hold definitionally at every input.

noncomputable def strassenForcing (n : ℕ) : ℝ := strassenWork n - 7 * strassenWork (n / 2)

The work function satisfies the Chapter 4 floor-division Master recurrence with a = 7, b = 2.

theorem strassenWork_floorRec : FloorDivideRecurrence 7 2 strassenForcing strassenWork := by refine ⟨fun n => ?_⟩ simp only [strassenForcing] push_cast ring

The work function is nonnegative.

theorem strassenWork_nonneg : ∀ n, 0 ≤ strassenWork n := by intro n induction n using Nat.strong_induction_on with | _ n ih => rcases Nat.eq_zero_or_pos n with hn | hn · subst hn; simp [strassenWork_zero] · rw [strassenWork_pos_step n hn] have hlt : n / 2 < n := Nat.div_lt_self hn (by norm_num) have hrec := ih (n / 2) hlt nlinarith [hrec, sq_nonneg ((n : ℕ) : ℝ)]

The work function is nondecreasing across one step.

theorem strassenWork_le_succ : ∀ n, strassenWork n ≤ strassenWork (n + 1) := by intro n induction n using Nat.strong_induction_on with | _ n ih => rcases Nat.eq_zero_or_pos n with hn | hn · subst hn; rw [strassenWork_zero]; exact strassenWork_nonneg _ · rw [strassenWork_pos_step n hn, strassenWork_pos_step (n + 1) (Nat.succ_pos n)] have hcast : ((n : ℕ) : ℝ) ^ 2 ≤ ((n + 1 : ℕ) : ℝ) ^ 2 := by have h1 : ((n : ℕ) : ℝ) ≤ ((n + 1 : ℕ) : ℝ) := by push_cast; linarith have hn_nonneg : (0 : ℝ) ≤ (n : ℕ) := Nat.cast_nonneg _ nlinarith [hn_nonneg, h1] rcases (by omega : (n + 1) / 2 = n / 2 ∨ (n + 1) / 2 = n / 2 + 1) with h | h · rw [h]; linarith [hcast] · rw [h] have hj : n / 2 < n := Nat.div_lt_self hn (by norm_num) have hstep := ih (n / 2) hj linarith [hstep, hcast]

The work function is monotone.

theorem strassenWork_monotone : Monotone strassenWork := monotone_nat_of_le_succ strassenWork_le_succ

The work function satisfies the absolute-value monotonicity interface.

theorem strassenWork_monotoneAbs : MonotoneAbs strassenWork := by intro m n hmn rw [abs_of_nonneg (strassenWork_nonneg m), abs_of_nonneg (strassenWork_nonneg n)] exact strassenWork_monotone hmn

The normalized forcing on exact powers is the convergent geometric sequence (4/7)^(k+1): on n = 2^(k+1) the forcing is exactly the block-work (2^(k+1))² = 4^(k+1), so dividing by 7^(k+1) gives (4/7)^(k+1). This is what places the Strassen recurrence in Master case 1.

theorem strassen_normForcing (k : ℕ) : normalizedForcing 7 2 strassenForcing k = (4 / 7 : ℝ) ^ (k + 1) := by have hpos : 0 < 2 ^ (k + 1) := pow_pos (by norm_num) _ have hdiv : 2 ^ (k + 1) / 2 = 2 ^ k := by rw [pow_succ]; omega have hcast : ((2 ^ (k + 1) : ℕ) : ℝ) ^ 2 = (4 : ℝ) ^ (k + 1) := by push_cast rw [show (4 : ℝ) = 2 ^ 2 by norm_num, ← pow_mul, ← pow_mul, Nat.mul_comm 2 (k + 1)] have hforcing : strassenForcing (2 ^ (k + 1)) = (4 : ℝ) ^ (k + 1) := by unfold strassenForcing rw [strassenWork_pos_step (2 ^ (k + 1)) hpos, hdiv] linarith [hcast] unfold normalizedForcing rw [hforcing, ← div_pow] norm_num

Case-1 hypothesis: the normalized forcing is nonnegative.

theorem strassen_term_nonneg (k : ℕ) : 0 ≤ normalizedForcing 7 2 strassenForcing k := by rw [strassen_normForcing]; positivity

Case-1 hypothesis: the normalized forcing is bounded by a geometric sequence.

theorem strassen_term_upper (k : ℕ) : normalizedForcing 7 2 strassenForcing k ≤ (4 / 7 : ℝ) * (4 / 7 : ℝ) ^ k := by rw [strassen_normForcing, pow_succ] exact le_of_eq (mul_comm _ _)

Value of the work recurrence at the base input 1.

theorem strassenWork_one : strassenWork 1 = 1 := by rw [show (1 : ℕ) = 0 + 1 from rfl, strassenWork_succ] norm_num [strassenWork_zero]

Positivity of the normalized base value, a case-1 hypothesis.

theorem strassen_base_pos : 0 < normalizedValue 7 2 strassenWork 0 := by unfold normalizedValue norm_num [strassenWork_one]

Runtime of Strassen's algorithm. The recurrence T(n) = 7 T(⌊n/2⌋) + n² is Θ(n^(log₂ 7)). This is the CLRS Θ(n^(lg 7)) bound, obtained by discharging Master-theorem case 1 (the forcing n² is polynomially smaller than the critical n^(log₂ 7)) through the Chapter 4 wrapper CLRS.­Chapter04.­floorDivide_allInput_masterCase1_realLogScale.

theorem strassen_runtime_bigTheta : Chapter03.isBigTheta strassenWork (realLogScale 7 2) := floorDivide_allInput_masterCase1_realLogScale 7 2 strassenForcing strassenWork strassenWork_floorRec (by norm_num) (by norm_num) strassenWork_monotoneAbs strassen_base_pos strassen_term_nonneg (r := 4 / 7) (C := 4 / 7) (by norm_num) (by norm_num) (by norm_num) strassen_term_upper

The comparison scale CLRS.­Chapter04.­realLogScale at a = 7, b = 2 is the textbook power n^(log₂ 7), so CLRS.­Chapter04.­strassen_runtime_bigTheta is exactly the CLRS Θ(n^(lg 7)) statement.

theorem realLogScale_seven_two (n : ℕ) : realLogScale 7 2 n = (n : ℝ) ^ Real.logb 2 7 := by rw [realLogScale, realLogExponent, Real.logb] norm_num
end Chapter04end CLRS

Definitions and proofs

CLRSLean.FourthEdition.Chapter_04.MatrixExecution.Asymptotics

Asymptotic scalar work on power-of-two squares

The functions below read counters from arbitrary families of input matrices. The asymptotic variable is depth k; the comparison functions use their actual side length 2^k. Consequently these statements do not extend the input type to all natural dimensions.

namespace CLRS.Chapter04.MatrixExecution private theorem cubic_side (k : Nat) : ((2 ^ k : Nat) : ℝ) ^ 3 = (8 : ℝ) ^ k := by rw [Nat.cast_pow, Nat.cast_ofNat, ← pow_mul, Nat.mul_comm k 3, pow_mul] norm_num private theorem strassen_side (k : Nat) : ((2 ^ k : Nat) : ℝ) ^ Real.logb 2 7 = (7 : ℝ) ^ k := by rw [Nat.cast_pow, Nat.cast_ofNat, ← Real.rpow_pow_comm (by norm_num)] rw [Real.rpow_logb (by norm_num) (by norm_num) (by norm_num)]

Every input family has cubic scalar work in its power-of-two side length.

theorem mulWithCost_theta (R : Type u) [Ring R] (A B : ∀ k, SqMat R k) : Chapter03.isBigTheta (fun k => ((mulWithCost R k (A k) (B k)).work : ℝ)) (fun k => ((2 ^ k : Nat) : ℝ) ^ 3) := by constructor · rw [Chapter03.isBigO_iff] refine ⟨2, by norm_num, 0, ?_⟩ intro k _ rw [mulWithCost_work, cubic_side, abs_of_nonneg (by positivity), abs_of_nonneg (by positivity)] exact_mod_cast (mulCount_bounds k).2 · rw [Chapter03.isBigOmega_iff] refine ⟨1, by norm_num, 0, ?_⟩ intro k _ rw [mulWithCost_work, cubic_side, abs_of_nonneg (by positivity), abs_of_nonneg (by positivity), one_mul] exact_mod_cast (mulCount_bounds k).1

Every input family has Strassen's scalar-work exponent on its side length.

theorem strassenWithCost_theta (R : Type u) [Ring R] (A B : ∀ k, SqMat R k) : Chapter03.isBigTheta (fun k => ((strassenWithCost R k (A k) (B k)).work : ℝ)) (fun k => ((2 ^ k : Nat) : ℝ) ^ Real.logb 2 7) := by constructor · rw [Chapter03.isBigO_iff] refine ⟨7, by norm_num, 0, ?_⟩ intro k _ rw [strassenWithCost_work, strassen_side, abs_of_nonneg (by positivity), abs_of_nonneg (by positivity)] exact_mod_cast (strassenCount_bounds k).2 · rw [Chapter03.isBigOmega_iff] refine ⟨1, by norm_num, 0, ?_⟩ intro k _ rw [strassenWithCost_work, strassen_side, abs_of_nonneg (by positivity), abs_of_nonneg (by positivity), one_mul] exact_mod_cast (strassenCount_bounds k).1
end CLRS.Chapter04.MatrixExecution

CLRSLean.FourthEdition.Chapter_04.MatrixExecution.Algorithms

Matrix multiplication with scalar work

Both algorithms obtain their value and work from the same recursion. Every matrix sum/difference is itself evaluated by the counted leaf traversal. Strassen's seven product results are shared; each result is computed and charged once, even when it contributes to more than one output block.

namespace CLRS.Chapter04.MatrixExecution

Eight recursive products and four counted output-block sums.

def mulWithCost (R : Type u) [Ring R] : ∀ k, SqMat R k → SqMat R k → Result R k | 0, x, y => ⟨x * y, 1⟩ | k + 1, A, B => let p1 := mulWithCost R k (A 0 0) (B 0 0) let p2 := mulWithCost R k (A 0 1) (B 1 0) let p3 := mulWithCost R k (A 0 0) (B 0 1) let p4 := mulWithCost R k (A 0 1) (B 1 1) let p5 := mulWithCost R k (A 1 0) (B 0 0) let p6 := mulWithCost R k (A 1 1) (B 1 0) let p7 := mulWithCost R k (A 1 0) (B 0 1) let p8 := mulWithCost R k (A 1 1) (B 1 1) let c11 := zipWithCost R (· + ·) k p1.value p2.value let c12 := zipWithCost R (· + ·) k p3.value p4.value let c21 := zipWithCost R (· + ·) k p5.value p6.value let c22 := zipWithCost R (· + ·) k p7.value p8.value ⟨!![c11.value, c12.value; c21.value, c22.value], p1.work + p2.work + p3.work + p4.work + p5.work + p6.work + p7.work + p8.work + c11.work + c12.work + c21.work + c22.work⟩

Strassen with all ten preparation and eight reassembly sums/differences counted.

def strassenWithCost (R : Type u) [Ring R] : ∀ k, SqMat R k → SqMat R k → Result R k | 0, x, y => ⟨x * y, 1⟩ | k + 1, A, B => let t1 := zipWithCost R (· - ·) k (B 0 1) (B 1 1) let t2 := zipWithCost R (· + ·) k (A 0 0) (A 0 1) let t3 := zipWithCost R (· + ·) k (A 1 0) (A 1 1) let t4 := zipWithCost R (· - ·) k (B 1 0) (B 0 0) let t5 := zipWithCost R (· + ·) k (A 0 0) (A 1 1) let t6 := zipWithCost R (· + ·) k (B 0 0) (B 1 1) let t7 := zipWithCost R (· - ·) k (A 0 1) (A 1 1) let t8 := zipWithCost R (· + ·) k (B 1 0) (B 1 1) let t9 := zipWithCost R (· - ·) k (A 0 0) (A 1 0) let t10 := zipWithCost R (· + ·) k (B 0 0) (B 0 1) let p1 := strassenWithCost R k (A 0 0) t1.value let p2 := strassenWithCost R k t2.value (B 1 1) let p3 := strassenWithCost R k t3.value (B 0 0) let p4 := strassenWithCost R k (A 1 1) t4.value let p5 := strassenWithCost R k t5.value t6.value let p6 := strassenWithCost R k t7.value t8.value let p7 := strassenWithCost R k t9.value t10.value let c11a := zipWithCost R (· + ·) k p5.value p4.value let c11b := zipWithCost R (· - ·) k c11a.value p2.value let c11 := zipWithCost R (· + ·) k c11b.value p6.value let c12 := zipWithCost R (· + ·) k p1.value p2.value let c21 := zipWithCost R (· + ·) k p3.value p4.value let c22a := zipWithCost R (· + ·) k p5.value p1.value let c22b := zipWithCost R (· - ·) k c22a.value p3.value let c22 := zipWithCost R (· - ·) k c22b.value p7.value ⟨!![c11.value, c12.value; c21.value, c22.value], t1.work + t2.work + t3.work + t4.work + t5.work + t6.work + t7.work + t8.work + t9.work + t10.work + p1.work + p2.work + p3.work + p4.work + p5.work + p6.work + p7.work + c11a.work + c11b.work + c11.work + c12.work + c21.work + c22a.work + c22b.work + c22.work⟩

Erasing work recovers the existing eight-product algorithm.

theorem mulWithCost_value (R : Type u) [Ring R] : ∀ (k : Nat) (A B : SqMat R k), (mulWithCost R k A B).value = mulRec R k A B := by intro k induction k with | zero => intros; rfl | succ k ih => intro A B simp only [mulWithCost, addWithCost_value, ih, mulRec]

Erasing work recovers the existing seven-product algorithm.

theorem strassenWithCost_value (R : Type u) [Ring R] : ∀ (k : Nat) (A B : SqMat R k), (strassenWithCost R k A B).value = strassenRec R k A B := by intro k induction k with | zero => intros; rfl | succ k ih => intro A B simp only [strassenWithCost, addWithCost_value, subWithCost_value, ih, strassenRec]

Depth recurrence proved below to be the eight-product execution's work.

def mulCount : Nat → Nat | 0 => 1 | k + 1 => 8 * mulCount k + 4 * 4 ^ k

Depth recurrence including Strassen's eighteen block sums/differences.

def strassenCount : Nat → Nat | 0 => 1 | k + 1 => 7 * strassenCount k + 18 * 4 ^ k
theorem mulWithCost_work (R : Type u) [Ring R] : ∀ (k : Nat) (A B : SqMat R k), (mulWithCost R k A B).work = mulCount k := by intro k induction k with | zero => intros; rfl | succ k ih => intro A B simp only [mulWithCost, zipWithCost_work, ih, mulCount] omegatheorem strassenWithCost_work (R : Type u) [Ring R] : ∀ (k : Nat) (A B : SqMat R k), (strassenWithCost R k A B).work = strassenCount k := by intro k induction k with | zero => intros; rfl | succ k ih => intro A B simp only [strassenWithCost, zipWithCost_work, ih, strassenCount] omegaend CLRS.Chapter04.MatrixExecution

CLRSLean.FourthEdition.Chapter_04.MatrixExecution.Basic

Scalar-operation execution for block matrices

The counter charges one scalar addition, subtraction, or multiplication. Matrix addition/subtraction visits all four child blocks recursively; it is not charged as one scalar operation. Indexing, immutable representation, allocation, and forming the four output blocks are outside this arithmetic model. Local results are shared when a later expression uses them more than once.

namespace CLRS.Chapter04.MatrixExecution

A returned matrix and the scalar arithmetic operations used to obtain it.

structure Result (R : Type u) (k : Nat) where value : SqMat R k work : Nat

Apply one scalar operation at every corresponding pair of leaves.

def zipWithCost (R : Type u) (op : R → R → R) : ∀ k, SqMat R k → SqMat R k → Result R k | 0, x, y => ⟨op x y, 1⟩ | k + 1, A, B => let a := zipWithCost R op k (A 0 0) (B 0 0) let b := zipWithCost R op k (A 0 1) (B 0 1) let c := zipWithCost R op k (A 1 0) (B 1 0) let d := zipWithCost R op k (A 1 1) (B 1 1) ⟨!![a.value, b.value; c.value, d.value], a.work + b.work + c.work + d.work⟩

The count comes from visiting the scalar leaves of the returned matrix.

theorem zipWithCost_work (R : Type u) (op : R → R → R) : ∀ (k : Nat) (A B : SqMat R k), (zipWithCost R op k A B).work = 4 ^ k := by intro k induction k with | zero => intros; rfl | succ k ih => intro A B simp only [zipWithCost, ih, pow_succ] omega
theorem addWithCost_value (R : Type u) [Ring R] : ∀ (k : Nat) (A B : SqMat R k), (zipWithCost R (· + ·) k A B).value = A + B := by intro k induction k with | zero => intros; rfl | succ k ih => intro A B funext i j fin_cases i <;> fin_cases j <;> simp [zipWithCost, ih] <;> rfltheorem subWithCost_value (R : Type u) [Ring R] : ∀ (k : Nat) (A B : SqMat R k), (zipWithCost R (· - ·) k A B).value = A - B := by intro k induction k with | zero => intros; rfl | succ k ih => intro A B funext i j fin_cases i <;> fin_cases j <;> simp [zipWithCost, ih] <;> rflend CLRS.Chapter04.MatrixExecution

CLRSLean.FourthEdition.Chapter_04.MatrixExecution.Bounds

Bounds for executed scalar arithmetic

These results concern matrices of side length 2^k. The eight-product counter equals the existing work recurrence on that domain. Strassen's eighteen block sums give a different exact count, bounded by constant multiples of the existing recurrence. Neither result claims an arbitrary-dimension padding API or a machine-time bound for ring operations of nonconstant cost.

namespace CLRS.Chapter04.MatrixExecution private theorem side_square (k : Nat) : ((2 ^ k : Nat) : ℝ) ^ 2 = (4 ^ k : Nat) := by norm_cast rw [← pow_mul, Nat.mul_comm k 2, pow_mul] norm_num theorem mulCount_eq_budget (k : Nat) : (mulCount k : ℝ) = mulWork (2 ^ k) := by induction k with | zero => simp [mulCount, mulWork_one] | succ k ih => rw [mulCount, Nat.cast_add, Nat.cast_mul, ih, mulWork_pos_step (2 ^ (k + 1)) (by positivity)] have hdiv : 2 ^ (k + 1) / 2 = 2 ^ k := by rw [pow_succ]; omega rw [hdiv, side_square] simp only [pow_succ 4 k, Nat.cast_mul, Nat.cast_ofNat] norm_num ring theorem strassenCount_budget_bounds (k : Nat) : strassenWork (2 ^ k) ≤ (strassenCount k : ℝ) ∧ (strassenCount k : ℝ) ≤ 5 * strassenWork (2 ^ k) := by induction k with | zero => norm_num [strassenCount, strassenWork_one] | succ k ih => rw [strassenCount, Nat.cast_add, Nat.cast_mul, strassenWork_pos_step (2 ^ (k + 1)) (by positivity)] have hdiv : 2 ^ (k + 1) / 2 = 2 ^ k := by rw [pow_succ]; omega rw [hdiv, side_square] simp only [pow_succ 4 k, Nat.cast_mul, Nat.cast_ofNat] have hnonneg : (0 : ℝ) ≤ (4 ^ k : Nat) := by positivity constructor <;> nlinarith [ih.1, ih.2]

Equality with the budget is proved from the returned execution counter.

theorem mulWithCost_work_eq (R : Type u) [Ring R] (k : Nat) (A B : SqMat R k) : ((mulWithCost R k A B).work : ℝ) = mulWork (2 ^ k) := by rw [mulWithCost_work, mulCount_eq_budget]

Strassen's actual arithmetic count is within a factor of five of the budget.

theorem strassenWithCost_work_bounds (R : Type u) [Ring R] (k : Nat) (A B : SqMat R k) : strassenWork (2 ^ k) ≤ ((strassenWithCost R k A B).work : ℝ) ∧ ((strassenWithCost R k A B).work : ℝ) ≤ 5 * strassenWork (2 ^ k) := by rw [strassenWithCost_work] exact strassenCount_budget_bounds k
theorem mulCount_closed (k : Nat) : mulCount k + 4 ^ k = 2 * 8 ^ k := by induction k with | zero => norm_num [mulCount] | succ k ih => simp only [mulCount, pow_succ]; omegatheorem strassenCount_closed (k : Nat) : strassenCount k + 6 * 4 ^ k = 7 * 7 ^ k := by induction k with | zero => norm_num [strassenCount] | succ k ih => simp only [strassenCount, pow_succ]; omega theorem mulCount_bounds (k : Nat) : 8 ^ k ≤ mulCount k ∧ mulCount k ≤ 2 * 8 ^ k := by have h := mulCount_closed k have hpow : 4 ^ k ≤ 8 ^ k := Nat.pow_le_pow_left (by norm_num) k have hnonneg := Nat.zero_le (4 ^ k) omega theorem strassenCount_bounds (k : Nat) : 7 ^ k ≤ strassenCount k ∧ strassenCount k ≤ 7 * 7 ^ k := by have h := strassenCount_closed k have hpow : 4 ^ k ≤ 7 ^ k := Nat.pow_le_pow_left (by norm_num) k omega

Value correctness and work for the same eight-product run.

theorem mulWithCost_correct (R : Type u) [Ring R] (k : Nat) (A B : SqMat R k) : (mulWithCost R k A B).value = A * B ∧ ((mulWithCost R k A B).work : ℝ) = mulWork (2 ^ k) := ⟨by rw [mulWithCost_value, mulRec_correct], mulWithCost_work_eq R k A B⟩

Value correctness and work bounds for the same seven-product run.

theorem strassenWithCost_correct (R : Type u) [Ring R] (k : Nat) (A B : SqMat R k) : (strassenWithCost R k A B).value = A * B ∧ strassenWork (2 ^ k) ≤ ((strassenWithCost R k A B).work : ℝ) ∧ ((strassenWithCost R k A B).work : ℝ) ≤ 5 * strassenWork (2 ^ k) := ⟨by rw [strassenWithCost_value, strassenRec_correct], strassenWithCost_work_bounds R k A B⟩
end CLRS.Chapter04.MatrixExecution

CLRSLean.Chapter_04.Section_04_6_Master_Theorem_All_Input

The real-log comparison scale n^(log_b a). This is the textbook scale used in the standard CLRS statement of the Master theorem: the homogeneous-solution growth rate without floors and ceilings.

For integer exponents it coincides with the ordinary polynomial scale polynomialScale.

noncomputable def realLogScale (a b : ℕ) (n : ℕ) : ℝ := (n : ℝ) ^ (realLogExponent a b)

Floor-division all-input Master case 1 stated in the textbook real-log scale n^(log_b a).

theorem floorDivide_allInput_masterCase1_realLogScale (a b : ℕ) (f T : ℕ → ℝ) (h_rec : FloorDivideRecurrence a b f T) (ha : 1 ≤ a) (hb : 1 < b) (hT_mono : MonotoneAbs T) (h_base_pos : 0 < normalizedValue a b T 0) (h_term_nonneg : ∀ k, 0 ≤ normalizedForcing a b f k) {r C : ℝ} (hr_nonneg : 0 ≤ r) (hr_lt_one : r < 1) (hC_pos : 0 < C) (h_term_upper : ∀ k, normalizedForcing a b f k ≤ C * r ^ k) : Chapter03.isBigTheta T (realLogScale a b) := by exact Chapter03.isBigTheta_trans (floorDivide_allInput_masterCase1_criticalPowerScale a b f T h_rec ha hb hT_mono h_base_pos h_term_nonneg hr_nonneg hr_lt_one hC_pos h_term_upper) (criticalPowerScale_isBigTheta_realLogScale a b ha hb)
Imports
import Mathlib.Tactic

4.3. The Substitution Method for Solving Recurrences

This file packages the CLRS substitution method as reusable induction principles for one-step recurrence bounds. The core idea is intentionally small: a guessed bound is a loop invariant over the recurrence index.

Main results:

  • Theorem CLRS.Chapter04.substitution_upper_bound: base plus step preservation proves an upper bound.

  • Theorem CLRS.Chapter04.substitution_lower_bound: the corresponding lower-bound principle.

  • Theorem CLRS.Chapter04.substitution_sandwich: simultaneous lower and upper bounds.

  • Theorems CLRS.Chapter04.linear_substitution_upper_bound and CLRS.Chapter04.geometric_substitution_upper_bound: ready-to-use CLRS-style templates for common recurrence guesses.

Status: proved for the substitution-method induction principles. Later divide-and-conquer sections instantiate these templates.

namespace CLRSnamespace Chapter04

A proposed upper bound for a cost function.

def IsUpperBound (T B : ℕ → ℝ) : Prop := ∀ n, T n ≤ B n

A proposed lower bound for a cost function.

def IsLowerBound (T B : ℕ → ℝ) : Prop := ∀ n, B n ≤ T n

The basic substitution-method upper-bound principle: prove the proposed bound at the base case, then prove that one recurrence step preserves it.

theorem substitution_upper_bound (T B : ℕ → ℝ) (h0 : T 0 ≤ B 0) (hstep : ∀ n, T n ≤ B n → T (n + 1) ≤ B (n + 1)) : IsUpperBound T B := by intro n induction n with | zero => exact h0 | succ n ih => exact hstep n ih

The lower-bound form of the substitution method. It has the same proof shape as the upper-bound theorem, but the guessed function is below the recurrence.

theorem substitution_lower_bound (T B : ℕ → ℝ) (h0 : B 0 ≤ T 0) (hstep : ∀ n, B n ≤ T n → B (n + 1) ≤ T (n + 1)) : IsLowerBound T B := by intro n induction n with | zero => exact h0 | succ n ih => exact hstep n ih

If the lower and upper guesses are both preserved by the recurrence step, then the true cost function is sandwiched between them for every index.

theorem substitution_sandwich (T L U : ℕ → ℝ) (hL0 : L 0 ≤ T 0) (hU0 : T 0 ≤ U 0) (hLstep : ∀ n, L n ≤ T n → L (n + 1) ≤ T (n + 1)) (hUstep : ∀ n, T n ≤ U n → T (n + 1) ≤ U (n + 1)) : IsLowerBound T L ∧ IsUpperBound T U := by constructor · exact substitution_lower_bound T L hL0 hLstep · exact substitution_upper_bound T U hU0 hUstep

Common recurrence templates

Linear additive upper bounds: if each step adds at most inc, then the cost is at most the base bound plus inc * n.

theorem linear_substitution_upper_bound (T : ℕ → ℝ) {base inc : ℝ} (h0 : T 0 ≤ base) (hstep : ∀ n, T (n + 1) ≤ T n + inc) : ∀ n : ℕ, T n ≤ base + inc * (n : ℝ) := by intro n induction n with | zero => simpa using h0 | succ n ih => calc T (n + 1) ≤ T n + inc := hstep n _ ≤ (base + inc * (n : ℝ)) + inc := by linarith _ = base + inc * ((n + 1 : ℕ) : ℝ) := by norm_num ring

Linear additive lower bounds: if each step adds at least inc, then the cost is at least the base bound plus inc * n.

theorem linear_substitution_lower_bound (T : ℕ → ℝ) {base inc : ℝ} (h0 : base ≤ T 0) (hstep : ∀ n, T n + inc ≤ T (n + 1)) : ∀ n : ℕ, base + inc * (n : ℝ) ≤ T n := by intro n induction n with | zero => simpa using h0 | succ n ih => calc base + inc * ((n + 1 : ℕ) : ℝ) = (base + inc * (n : ℝ)) + inc := by norm_num ring _ ≤ T n + inc := by linarith _ ≤ T (n + 1) := hstep n

Geometric upper bounds: a nonnegative multiplicative step preserves a guessed bound of the form base * a^n.

theorem geometric_substitution_upper_bound (T : ℕ → ℝ) {base a : ℝ} (ha_nonneg : 0 ≤ a) (h0 : T 0 ≤ base) (hstep : ∀ n, T (n + 1) ≤ a * T n) : ∀ n : ℕ, T n ≤ base * a ^ n := by intro n induction n with | zero => simpa using h0 | succ n ih => calc T (n + 1) ≤ a * T n := hstep n _ ≤ a * (base * a ^ n) := by gcongr _ = base * a ^ (n + 1) := by rw [pow_succ] ring

Geometric lower bounds, dual to CLRS.Chapter04.geometric_substitution_upper_bound.

theorem geometric_substitution_lower_bound (T : ℕ → ℝ) {base a : ℝ} (ha_nonneg : 0 ≤ a) (h0 : base ≤ T 0) (hstep : ∀ n, a * T n ≤ T (n + 1)) : ∀ n : ℕ, base * a ^ n ≤ T n := by intro n induction n with | zero => simpa using h0 | succ n ih => calc base * a ^ (n + 1) = a * (base * a ^ n) := by rw [pow_succ] ring _ ≤ a * T n := by gcongr _ ≤ T (n + 1) := hstep n
end Chapter04end CLRS
Imports
open Finsetopen scoped BigOperators

4.4. The Recursion-Tree Method for Solving Recurrences

This file makes the finite-sum core of the CLRS recursion-tree method explicit.

Main results:

  • Theorem CLRS.Chapter04.recursion_tree_additive_unroll: an additive one-step recurrence is exactly the base value plus the sum of level costs.

  • Theorem CLRS.Chapter04.recursion_tree_additive_upper_envelope: if each level cost is bounded by an envelope, the whole tree is bounded by the sum of the envelope.

  • Theorem CLRS.Chapter04.recursion_tree_constant_level_cost: constant level costs give the usual linear closed form.

  • Theorem CLRS.Chapter04.BranchingRecursionTree.totalCost_eq_levelCosts_add_leafCost: an explicit finite branching tree decomposes into internal level costs plus leaf costs.

  • Theorems CLRS.Chapter04.balancedThreeQuarter_totalCost_le and CLRS.Chapter04.unbalancedThirdTwoThird_totalCost: the two detailed textbook examples have respectively geometric and constant level costs.

  • Theorem CLRS.Chapter04.IntegerBranchingSpec.build_totalCost_eq: an explicit finite natural-size tree has exactly the value of its independently stated floor/ceiling recurrence, even when branches stop at unequal depths.

  • Theorems CLRS.Chapter04.balancedIntegerTree_totalCost_eq and CLRS.Chapter04.unbalancedIntegerTree_totalCost_eq: arbitrary-input integer instances of the two detailed textbook examples.

  • Theorems CLRS.Chapter04.balancedIntegerCost_isBigTheta and CLRS.Chapter04.unbalancedIntegerCost_isBigTheta: actual rounded-tree total costs are Θ(n²) and Θ(n log n) for positive local-cost coefficients and nonnegative base costs.

Status: proved for the finite-sum core, the fixed-depth level-sum model, and the explicit unequal-depth integer floor/ceiling trees. The exact tree semantics equal independently stated rounded recurrences. Strong induction on these recurrences proves the two actual execution bounds, including unequal child depths. This does not claim a general theorem for arbitrary rounded branching recurrences.

namespace CLRSnamespace Chapter04

Unroll an additive recurrence into the sum of its level costs.

theorem recursion_tree_additive_unroll (T cost : ℕ → ℝ) (hstep : ∀ n, T (n + 1) = T n + cost n) (n : ℕ) : T n = T 0 + ∑ k ∈ range n, cost k := by induction n with | zero => simp | succ n ih => rw [hstep n, ih] simp [sum_range_succ, add_assoc]

If every level cost is bounded by an envelope, then the unrolled recursion tree is bounded by the sum of that envelope.

theorem recursion_tree_additive_upper_envelope (T cost envelope : ℕ → ℝ) (hcost : ∀ k, cost k ≤ envelope k) (hstep : ∀ n, T (n + 1) = T n + cost n) (n : ℕ) : T n ≤ T 0 + ∑ k ∈ range n, envelope k := by rw [recursion_tree_additive_unroll T cost hstep n] exact add_le_add le_rfl (Finset.sum_le_sum (fun k _hk => hcost k))

If every level cost is at least an envelope, then the same unrolling gives a lower bound by the envelope sum.

theorem recursion_tree_additive_lower_envelope (T cost envelope : ℕ → ℝ) (hcost : ∀ k, envelope k ≤ cost k) (hstep : ∀ n, T (n + 1) = T n + cost n) (n : ℕ) : T 0 + ∑ k ∈ range n, envelope k ≤ T n := by rw [recursion_tree_additive_unroll T cost hstep n] exact add_le_add le_rfl (Finset.sum_le_sum (fun k _hk => hcost k))

Constant level costs collapse the recursion tree to the base value plus a linear term.

theorem recursion_tree_constant_level_cost (T : ℕ → ℝ) {level : ℝ} (hstep : ∀ n, T (n + 1) = T n + level) : ∀ n : ℕ, T n = T 0 + level * (n : ℝ) := by intro n rw [recursion_tree_additive_unroll T (fun _ => level) hstep n] simp [Finset.sum_const, nsmul_eq_mul, mul_comm]

A bounded-cost recursion tree with at most level work per depth is bounded by the base bound plus level * n.

theorem recursion_tree_constant_upper_bound (T cost : ℕ → ℝ) {base level : ℝ} (hbase : T 0 ≤ base) (hcost : ∀ k, cost k ≤ level) (hstep : ∀ n, T (n + 1) = T n + cost n) : ∀ n : ℕ, T n ≤ base + level * (n : ℝ) := by intro n have hsum := recursion_tree_additive_upper_envelope T cost (fun _ => level) hcost hstep n calc T n ≤ T 0 + ∑ _k ∈ range n, level := hsum _ ≤ base + ∑ _k ∈ range n, level := by linarith _ = base + level * (n : ℝ) := by simp [Finset.sum_const, nsmul_eq_mul, mul_comm]
end Chapter04end CLRS

Definitions and proofs

CLRSLean.FourthEdition.Chapter_04.Section_04_4_Recursion_Tree_Method.Branching.IntegerTree.Asymptotics

Connections to Chapter 4 asymptotic interfaces

The costs of the generated rounded trees satisfy the textbook bounds on all natural inputs: the balanced tree has quadratic cost, and the unequal-depth tree has Θ(n log n) cost. The proofs use their actual recurrence equations, including the floor and ceiling operations and base cases.

namespace CLRSnamespace Chapter04

The integer unequal-depth example uses the classic Akra--Bazzi root p=1.

theorem unbalancedInteger_akraBazziRoot : IsAkraBazziRoot [(1, (3 : Real)), (1, (3 : Real) / 2)] 1 := akraBazziRoot_two_thirds_one

The balanced tree has nonnegative cost and a quadratic upper potential, including zero-size leaves.

theorem balancedIntegerCost_bounds {c base : ℝ} (hc : 0 ≤ c) (hb : 0 ≤ base) (n : ℕ) : 0 ≤ balancedIntegerCost c base n ∧ balancedIntegerCost c base n ≤ (4*c+base)*((n : ℝ)+1)^2 := by induction n using Nat.strong_induction_on with | h n ih => by_cases hn : n ≤ 1 · rw [balancedIntegerCost_base hn] constructor · exact hb · have hsq : 1 ≤ ((n : ℝ)+1)^2 := by nlinarith [Nat.cast_nonneg (α := ℝ) n] have hmul := mul_le_mul_of_nonneg_left hsq (show 0 ≤ 4*c+base by positivity) nlinarith · have hn' : 1 < n := by omega have hq : n/4 < n := Nat.div_lt_self (by omega) (by norm_num) obtain ⟨hl, hu⟩ := ih (n/4) hq rw [balancedIntegerCost_step hn'] constructor · positivity · have hhalfNat : 2*(n/4+1) ≤ n+1 := by omega have hhalf : 2*((n/4 : ℕ)+1 : ℝ) ≤ (n : ℝ)+1 := by exact_mod_cast hhalfNat have hq0 : 0 ≤ ((n/4 : ℕ) : ℝ) := by positivity have hn0 : 0 ≤ (n : ℝ) := by positivity have hK0 : 0 ≤ 4*c+base := by positivity have hsq : 4*(((n/4 : ℕ) : ℝ)+1)^2 ≤ ((n : ℝ)+1)^2 := by nlinarith have hw := mul_le_mul_of_nonneg_left hsq hK0 have hlocal := mul_nonneg hc (show 0 ≤ ((n : ℝ)+1)^2-(n : ℝ)^2 by nlinarith) have hbase := mul_nonneg hb (sq_nonneg ((n : ℝ)+1)) nlinarith

The generated balanced rounded tree has Θ(n²) cost whenever its quadratic local charge is positive and its leaf charge is nonnegative.

theorem balancedIntegerCost_isBigTheta {c base : ℝ} (hc : 0 < c) (hb : 0 ≤ base) : Chapter03.isBigTheta (balancedIntegerCost c base) (fun n : ℕ => (n : ℝ)^2) := by constructor · apply (Chapter03.isBigO_iff _ _).mpr refine ⟨4*(4*c+base), by positivity, 2, ?_⟩ intro n hn obtain ⟨hl, hu⟩ := balancedIntegerCost_bounds hc.le hb n rw [abs_of_nonneg hl, abs_of_nonneg (sq_nonneg _)] have hn1 : 1 ≤ (n : ℝ) := by exact_mod_cast (show 1 ≤ n by omega) have hsq : ((n : ℝ)+1)^2 ≤ 4*(n : ℝ)^2 := by nlinarith have hw := mul_le_mul_of_nonneg_left hsq (show 0 ≤ 4*c+base by positivity) nlinarith · apply (Chapter03.isBigOmega_iff _ _).mpr refine ⟨c, hc, 2, ?_⟩ intro n hn rw [abs_of_nonneg (balancedIntegerCost_bounds hc.le hb n).1, abs_of_nonneg (sq_nonneg _), balancedIntegerCost_step (by omega)] have hchild := (balancedIntegerCost_bounds hc.le hb (n/4)).1 linarith
private theorem rounded_thirds_bounds (n : ℕ) (hn : 2 < n) : n/3 + twoThirdsCeil n = n ∧ 0 < n/3 ∧ 0 < twoThirdsCeil n ∧ n ≤ 5*(n/3) ∧ n ≤ 5*twoThirdsCeil n ∧ 5*(n/3) ≤ 4*n ∧ 5*twoThirdsCeil n ≤ 4*n := by rw [twoThirdsCeil, Nat.ceilDiv_eq_add_pred_div] by_cases hsmall : n < 6 · interval_cases n <;> norm_num at * · omega private theorem split_log_gap (n x y : ℝ) (hx : 0 < x) (hy : 0 < y) (hs : x+y=n) (hlx : n ≤ 5*x) (hly : n ≤ 5*y) (hux : 5*x ≤ 4*n) (huy : 5*y ≤ 4*n) : n/5 ≤ n*Real.log n-x*Real.log x-y*Real.log y ∧ n*Real.log n-x*Real.log x-y*Real.log y ≤ 4*n := by have hn : 0 < n := by linarith have hlogLow : (1:ℝ)/5 ≤ Real.log ((5:ℝ)/4) := by have h := Real.one_sub_inv_le_log_of_pos (x := (5:ℝ)/4) (by norm_num) norm_num at h ⊢ exact h have hlogHigh : Real.log (5:ℝ) ≤ 4 := by have h := Real.log_le_sub_one_of_pos (x := (5:ℝ)) (by norm_num) norm_num at h ⊢ exact h have hlogs (z : ℝ) (hz : 0 < z) (hl : n ≤ 5*z) (hu : 5*z ≤ 4*n) : (1:ℝ)/5 ≤ Real.log n-Real.log z ∧ Real.log n-Real.log z ≤ 4 := by have hlo : (5:ℝ)/4 ≤ n/z := (le_div_iff₀ hz).mpr (by nlinarith) have hhi : n/z ≤ 5 := (div_le_iff₀ hz).mpr hl rw [← Real.log_div hn.ne' hz.ne'] exact ⟨hlogLow.trans (Real.log_le_log (by norm_num) hlo), (Real.log_le_log (div_pos hn hz) hhi).trans hlogHigh⟩ have hxl := mul_le_mul_of_nonneg_left (hlogs x hx hlx hux).1 hx.le have hxu := mul_le_mul_of_nonneg_left (hlogs x hx hlx hux).2 hx.le have hyl := mul_le_mul_of_nonneg_left (hlogs y hy hly huy).1 hy.le have hyu := mul_le_mul_of_nonneg_left (hlogs y hy hly huy).2 hy.le have heq : n*Real.log n-x*Real.log x-y*Real.log y = x*(Real.log n-Real.log x)+y*(Real.log n-Real.log y) := by rw [← hs] ring rw [heq] constructor <;> nlinarith

Explicit affine logarithmic potentials for the actual unequal-depth tree. The linear terms absorb the base cases and cancel at each rounded split.

theorem unbalancedIntegerCost_bounds {c base : ℝ} (hc : 0 ≤ c) (hb : 0 ≤ base) (n : ℕ) (hn : 1 ≤ n) : 0 ≤ unbalancedIntegerCost c base n ∧ (c/4)*(n : ℝ)*(Real.log (n : ℝ)-1) ≤ unbalancedIntegerCost c base n ∧ unbalancedIntegerCost c base n ≤ 5*c*(n : ℝ)*Real.log (n : ℝ)+base*(n : ℝ) := by induction n using Nat.strong_induction_on with | h n ih => by_cases hbase : n ≤ 2 · rw [unbalancedIntegerCost_base hbase] have hlog2 : Real.log (2:ℝ) ≤ 1 := by have h := Real.log_le_sub_one_of_pos (x := (2:ℝ)) (by norm_num) norm_num at h ⊢ exact h have hlog2pos : 0 ≤ Real.log (2:ℝ) := Real.log_nonneg (by norm_num) interval_cases n <;> norm_num at * · exact ⟨hb, by nlinarith⟩ · exact ⟨hb, by nlinarith [mul_nonneg hc (sub_nonneg.mpr hlog2)], by nlinarith [mul_nonneg hc hlog2pos]⟩ · have hrec : 2 < n := by omega obtain ⟨hs, hx, hy, hlx, hly, hux, huy⟩ := rounded_thirds_bounds n hrec have hsR : ((n/3 : ℕ) : ℝ)+(twoThirdsCeil n : ℝ)=(n : ℝ) := by exact_mod_cast hs have hgap := split_log_gap (n : ℝ) ((n/3 : ℕ) : ℝ) (twoThirdsCeil n : ℝ) (by exact_mod_cast hx) (by exact_mod_cast hy) hsR (by exact_mod_cast hlx) (by exact_mod_cast hly) (by exact_mod_cast hux) (by exact_mod_cast huy) obtain ⟨hx0, hxL, hxU⟩ := ih (n/3) (thirdFloor_lt_self hrec) (by omega) obtain ⟨hy0, hyL, hyU⟩ := ih (twoThirdsCeil n) (twoThirdsCeil_lt_self hrec) (by omega) rw [unbalancedIntegerCost_step hrec] refine ⟨by positivity, ?_, ?_⟩ · have hw := mul_le_mul_of_nonneg_left hgap.2 (show 0 ≤ c/4 by positivity) have hcancel : (c/4)*((n/3 : ℕ) : ℝ)+(c/4)*(twoThirdsCeil n : ℝ)= (c/4)*(n : ℝ) := by rw [← mul_add, hsR] nlinarith · have hw := mul_le_mul_of_nonneg_left hgap.1 (show 0 ≤ 5*c by positivity) have hcancel : base*((n/3 : ℕ) : ℝ)+base*(twoThirdsCeil n : ℝ)= base*(n : ℝ) := by rw [← mul_add, hsR] nlinarith
private theorem two_le_log_of_sixteen_le {n : ℕ} (hn : 16 ≤ n) : 2 ≤ Real.log (n : ℝ) := by have hhalf := Real.one_sub_inv_le_log_of_pos (x := (2:ℝ)) (by norm_num) have hm := Real.log_le_log (by norm_num : (0:ℝ)<16) (show (16:ℝ) ≤ n by exact_mod_cast hn) have hpow : Real.log (16:ℝ) = 4*Real.log (2:ℝ) := by rw [show (16:ℝ)=2^4 by norm_num, Real.log_pow] norm_num rw [hpow] at hm norm_num at hhalf linarith

The generated floor/ceiling unequal-depth tree has Θ(n log n) cost. This follows from its recurrence and rounding arithmetic, without an assumed asymptotic comparison or an Akra--Bazzi certificate.

theorem unbalancedIntegerCost_isBigTheta {c base : ℝ} (hc : 0 < c) (hb : 0 ≤ base) : Chapter03.isBigTheta (unbalancedIntegerCost c base) (fun n : ℕ => (n : ℝ)*Real.log (n : ℝ)) := by constructor · apply (Chapter03.isBigO_iff _ _).mpr refine ⟨5*c+base, by positivity, 16, ?_⟩ intro n hn obtain ⟨h0, _, hU⟩ := unbalancedIntegerCost_bounds hc.le hb n (by omega) have hlog := two_le_log_of_sixteen_le hn rw [abs_of_nonneg h0, abs_of_nonneg (by positivity)] have hw := mul_nonneg (mul_nonneg hb (Nat.cast_nonneg n)) (show 0 ≤ Real.log (n : ℝ)-1 by linarith) nlinarith · apply (Chapter03.isBigOmega_iff _ _).mpr refine ⟨c/8, by positivity, 16, ?_⟩ intro n hn obtain ⟨h0, hL, _⟩ := unbalancedIntegerCost_bounds hc.le hb n (by omega) have hlog := two_le_log_of_sixteen_le hn rw [abs_of_nonneg h0, abs_of_nonneg (by positivity)] have hw := mul_nonneg (mul_nonneg hc.le (Nat.cast_nonneg n)) (show 0 ≤ Real.log (n : ℝ)-2 by linarith) nlinarith
end Chapter04end CLRS

CLRSLean.FourthEdition.Chapter_04.Section_04_4_Recursion_Tree_Method.Branching.IntegerTree.Balanced

The integer tree for 3T(n/4) + c n²

This is the rounded, arbitrary-input counterpart of the existing fixed-depth real-scale level calculation.

namespace CLRSnamespace Chapter04open Finsetopen scoped BigOperators

Three equal floor-divided children, with base cases at sizes zero and one.

def balancedIntegerSpec (c base : Real) : IntegerBranchingSpec (Fin 3) where cutoff := 1 childSize := fun _ n => n / 4 localCost := fun n => c * (n : Real) ^ 2 baseCost := fun _ => base decreases := by intro _ n hn exact Nat.div_lt_self (by omega) (by norm_num)

Independent textbook equations for the rounded balanced recurrence.

structure BalancedIntegerRecurrence (c base : Real) (T : Nat → Real) : Prop where base_eq : ∀ n, n ≤ 1 → T n = base step_eq : ∀ n, 1 < n → T n = 3 * T (n / 4) + c * (n : Real) ^ 2

The textbook equations instantiate the generic finite-tree semantics.

theorem BalancedIntegerRecurrence.satisfies {c base : Real} {T : Nat → Real} (hT : BalancedIntegerRecurrence c base T) : (balancedIntegerSpec c base).Satisfies T := by constructor · intro n hn exact hT.base_eq n hn · intro n hn rw [hT.step_eq n hn] simp [balancedIntegerSpec] ring

Exact equality between the rounded recurrence and its explicit tree.

theorem balancedIntegerTree_totalCost_eq {c base : Real} {T : Nat → Real} (hT : BalancedIntegerRecurrence c base T) (n : Nat) : IntegerBranchingTree.totalCost ((balancedIntegerSpec c base).build n) = T n := IntegerBranchingSpec.build_totalCost_eq _ _ hT.satisfies n

Cost function computed by the generated balanced integer tree.

def balancedIntegerCost (c base : Real) (n : Nat) : Real := IntegerBranchingTree.totalCost ((balancedIntegerSpec c base).build n)
@[simp] theorem balancedIntegerCost_base {c base : Real} {n : Nat} (hn : n ≤ 1) : balancedIntegerCost c base n = base := by simp [balancedIntegerCost, balancedIntegerSpec, hn] theorem balancedIntegerCost_step {c base : Real} {n : Nat} (hn : 1 < n) : balancedIntegerCost c base n = 3 * balancedIntegerCost c base (n / 4) + c * (n : Real) ^ 2 := by rw [balancedIntegerCost, IntegerBranchingSpec.build_of_lt _ _ hn] simp only [IntegerBranchingTree.totalCost_node] simp [balancedIntegerSpec, balancedIntegerCost] ringtheorem balancedIntegerCost_satisfies (c base : Real) : BalancedIntegerRecurrence c base (balancedIntegerCost c base) := by constructor · intro n hn exact balancedIntegerCost_base hn · intro n hn exact balancedIntegerCost_step hn

Forcing term that makes the generated tree cost an all-input floor recurrence, including the explicitly represented base cases.

def balancedIntegerForcing (c base : Real) (n : Nat) : Real := balancedIntegerCost c base n - 3 * balancedIntegerCost c base (n / 4)

Direct connection from the explicit tree to the all-input §4.6 interface.

theorem balancedIntegerCost_floorRecurrence (c base : Real) : FloorDivideRecurrence 3 4 (balancedIntegerForcing c base) (balancedIntegerCost c base) := by constructor intro n simp [balancedIntegerForcing]

Above the cutoff, the all-input forcing is exactly the textbook c n².

theorem balancedIntegerForcing_of_lt {c base : Real} {n : Nat} (hn : 1 < n) : balancedIntegerForcing c base n = c * (n : Real) ^ 2 := by rw [balancedIntegerForcing, balancedIntegerCost_step hn] ring
end Chapter04end CLRS

CLRSLean.FourthEdition.Chapter_04.Section_04_4_Recursion_Tree_Method.Branching.IntegerTree.Execution

Building integer branching trees

A specification contains the termination proof for every rounded child. The recurrence equation is stated independently, then strong induction identifies its solution with the cost of the generated finite tree.

namespace CLRSnamespace Chapter04open Finsetopen scoped BigOperators

Data needed to expand a natural-size recurrence into a finite tree.

structure IntegerBranchingSpec (Branch : Type) where cutoff : Nat childSize : Branch → Nat → Nat localCost : Nat → Real baseCost : Nat → Real decreases : ∀ branch n, cutoff < n → childSize branch n < n
namespace IntegerBranchingSpecvariable {Branch : Type}

The recurrence semantics, stated without referring to the generated tree.

structure Satisfies [Fintype Branch] (spec : IntegerBranchingSpec Branch) (T : Nat → Real) : Prop where base : ∀ n, n ≤ spec.cutoff → T n = spec.baseCost n step : ∀ n, spec.cutoff < n → T n = spec.localCost n + ∑ branch, T (spec.childSize branch n)

Executably expand all children until their independently certified cutoff.

def build (spec : IntegerBranchingSpec Branch) (n : Nat) : IntegerBranchingTree Branch := if _h : n ≤ spec.cutoff then .leaf n (spec.baseCost n) else .node n (spec.localCost n) (fun branch => spec.build (spec.childSize branch n)) termination_by n decreasing_by exact spec.decreases _ _ (Nat.lt_of_not_ge _h)
@[simp] theorem build_of_le (spec : IntegerBranchingSpec Branch) (n : Nat) (h : n ≤ spec.cutoff) : spec.build n = .leaf n (spec.baseCost n) := by rw [build] simp [h] @[simp] theorem build_of_lt (spec : IntegerBranchingSpec Branch) (n : Nat) (h : spec.cutoff < n) : spec.build n = .node n (spec.localCost n) (fun branch => spec.build (spec.childSize branch n)) := by rw [build] simp [Nat.not_le_of_gt h]

Building preserves the requested root subproblem size.

@[simp] theorem rootSize_build (spec : IntegerBranchingSpec Branch) (n : Nat) : IntegerBranchingTree.rootSize (spec.build n) = n := by by_cases h : n ≤ spec.cutoff · simp [build_of_le spec n h] · simp [build_of_lt spec n (Nat.lt_of_not_ge h)]

Predicate saying that every leaf size satisfies a given property.

def EveryLeafSize (P : Nat → Prop) : IntegerBranchingTree Branch → Prop | .leaf size _ => P size | .node _ _ children => ∀ branch, EveryLeafSize P (children branch)

Predicate saying that every internal-node size satisfies a property.

def EveryInternalSize (P : Nat → Prop) : IntegerBranchingTree Branch → Prop | .leaf _ _ => True | .node size _ children => P size ∧ ∀ branch, EveryInternalSize P (children branch)

Every generated leaf is genuinely in the base-case range.

theorem build_everyLeaf_le (spec : IntegerBranchingSpec Branch) (n : Nat) : EveryLeafSize (fun size => size ≤ spec.cutoff) (spec.build n) := by induction n using Nat.strong_induction_on with | h n ih => by_cases hbase : n ≤ spec.cutoff · simp [EveryLeafSize, hbase] · have hrec : spec.cutoff < n := Nat.lt_of_not_ge hbase rw [build_of_lt spec n hrec] simp only [EveryLeafSize] intro branch exact ih (spec.childSize branch n) (spec.decreases branch n hrec)

Every generated internal node is genuinely above the cutoff.

theorem build_everyInternal_gt (spec : IntegerBranchingSpec Branch) (n : Nat) : EveryInternalSize (fun size => spec.cutoff < size) (spec.build n) := by induction n using Nat.strong_induction_on with | h n ih => by_cases hbase : n ≤ spec.cutoff · simp [build_of_le spec n hbase, EveryInternalSize] · have hrec : spec.cutoff < n := Nat.lt_of_not_ge hbase rw [build_of_lt spec n hrec] simp only [EveryInternalSize] refine ⟨hrec, fun branch => ?_⟩ exact ih (spec.childSize branch n) (spec.decreases branch n hrec)

Exact recurrence-tree semantics. This is not true by definition: T is specified only by its base and recursive equations, independently of build.

theorem build_totalCost_eq [Fintype Branch] (spec : IntegerBranchingSpec Branch) (T : Nat → Real) (hT : spec.Satisfies T) (n : Nat) : IntegerBranchingTree.totalCost (spec.build n) = T n := by induction n using Nat.strong_induction_on with | h n ih => by_cases hbase : n ≤ spec.cutoff · rw [build_of_le spec n hbase] simp [hT.base n hbase] · have hrec : spec.cutoff < n := Nat.lt_of_not_ge hbase rw [build_of_lt spec n hrec] simp only [IntegerBranchingTree.totalCost_node] have hchildren : ∀ branch, IntegerBranchingTree.totalCost (spec.build (spec.childSize branch n)) = T (spec.childSize branch n) := fun branch => ih (spec.childSize branch n) (spec.decreases branch n hrec) simp_rw [hchildren] exact (hT.step n hrec).symm
end IntegerBranchingSpecend Chapter04end CLRS

CLRSLean.FourthEdition.Chapter_04.Section_04_4_Recursion_Tree_Method.Branching.IntegerTree.Model

Unequal-depth integer branching trees

This datatype records the actual natural-number size and work of every node. It is intentionally not indexed by a common depth: floor and ceiling branches may reach the base case at different times.

namespace CLRSnamespace Chapter04open Finsetopen scoped BigOperators

A finite recursion tree whose branches need not have the same height.

inductive IntegerBranchingTree (Branch : Type) | leaf (size : Nat) (work : Real) | node (size : Nat) (work : Real) (children : Branch → IntegerBranchingTree Branch)
namespace IntegerBranchingTreevariable {Branch : Type}

Natural-number subproblem size stored at the root.

def rootSize : IntegerBranchingTree Branch → Nat | .leaf size _ => size | .node size _ _ => size

Work stored at the root, whether it is a base or recursive node.

def rootWork : IntegerBranchingTree Branch → Real | .leaf _ work => work | .node _ work _ => work

Work stored in every node of the finite tree.

def totalCost [Fintype Branch] : IntegerBranchingTree Branch → Real | .leaf _ work => work | .node _ work children => work + ∑ branch, totalCost (children branch)

Maximum number of recursive edges on a root-to-leaf path.

def height [Fintype Branch] [DecidableEq Branch] : IntegerBranchingTree Branch → Nat | .leaf _ _ => 0 | .node _ _ children => 1 + Finset.univ.sup (fun branch => height (children branch))
@[simp] theorem rootSize_leaf (size : Nat) (work : Real) : rootSize (.leaf size work : IntegerBranchingTree Branch) = size := rfl@[simp] theorem rootSize_node (size : Nat) (work : Real) (children : Branch → IntegerBranchingTree Branch) : rootSize (.node size work children) = size := rfl@[simp] theorem rootWork_leaf (size : Nat) (work : Real) : rootWork (.leaf size work : IntegerBranchingTree Branch) = work := rfl@[simp] theorem rootWork_node (size : Nat) (work : Real) (children : Branch → IntegerBranchingTree Branch) : rootWork (.node size work children) = work := rfl@[simp] theorem totalCost_leaf [Fintype Branch] (size : Nat) (work : Real) : totalCost (.leaf size work : IntegerBranchingTree Branch) = work := rfl@[simp] theorem totalCost_node [Fintype Branch] (size : Nat) (work : Real) (children : Branch → IntegerBranchingTree Branch) : totalCost (.node size work children) = work + ∑ branch, totalCost (children branch) := rfl@[simp] theorem height_leaf [Fintype Branch] [DecidableEq Branch] (size : Nat) (work : Real) : height (.leaf size work : IntegerBranchingTree Branch) = 0 := rfl@[simp] theorem height_node [Fintype Branch] [DecidableEq Branch] (size : Nat) (work : Real) (children : Branch → IntegerBranchingTree Branch) : height (.node size work children) = 1 + Finset.univ.sup (fun branch => height (children branch)) := rflend IntegerBranchingTreeend Chapter04end CLRS

CLRSLean.FourthEdition.Chapter_04.Section_04_4_Recursion_Tree_Method.Branching.IntegerTree.Unbalanced

The integer tree for T(n/3) + T(2n/3) + c n

The larger child uses natural ceiling division. The generated tree therefore has no artificial common depth.

namespace CLRSnamespace Chapter04open Finsetopen scoped BigOperators

The textbook rounded size ceil(2n/3).

def twoThirdsCeil (n : Nat) : Nat := (2 * n) ⌈/⌉ 3

The smaller floor branch decreases above the chosen cutoff.

theorem thirdFloor_lt_self {n : Nat} (hn : 2 < n) : n / 3 < n := by exact Nat.div_lt_self (by omega) (by norm_num)

The larger ceiling branch also decreases above the chosen cutoff.

theorem twoThirdsCeil_lt_self {n : Nat} (hn : 2 < n) : twoThirdsCeil n < n := by rw [twoThirdsCeil, Nat.ceilDiv_eq_add_pred_div] omega

Floor and ceiling differ by at most one for the two-thirds child.

theorem twoThirds_floor_ceil_sandwich (n : Nat) : (2 * n) / 3 ≤ twoThirdsCeil n ∧ twoThirdsCeil n ≤ (2 * n) / 3 + 1 := by constructor · rw [twoThirdsCeil, Nat.ceilDiv_eq_add_pred_div] omega · rw [twoThirdsCeil, Nat.ceilDiv_eq_add_pred_div] omega

Two differently rounded children, with base cases through size two.

def unbalancedIntegerSpec (c base : Real) : IntegerBranchingSpec Bool where cutoff := 2 childSize := fun branch n => if branch then twoThirdsCeil n else n / 3 localCost := fun n => c * (n : Real) baseCost := fun _ => base decreases := by intro branch n hn cases branch with | false => simpa using thirdFloor_lt_self hn | true => simpa using twoThirdsCeil_lt_self hn

Independent textbook equations for the rounded unbalanced recurrence.

structure UnbalancedIntegerRecurrence (c base : Real) (T : Nat → Real) : Prop where base_eq : ∀ n, n ≤ 2 → T n = base step_eq : ∀ n, 2 < n → T n = T (n / 3) + T (twoThirdsCeil n) + c * (n : Real)

The textbook equations instantiate the generic finite-tree semantics.

theorem UnbalancedIntegerRecurrence.satisfies {c base : Real} {T : Nat → Real} (hT : UnbalancedIntegerRecurrence c base T) : (unbalancedIntegerSpec c base).Satisfies T := by constructor · intro n hn exact hT.base_eq n hn · intro n hn rw [hT.step_eq n hn] simp [unbalancedIntegerSpec] ring

Exact equality between the rounded recurrence and its explicit tree.

theorem unbalancedIntegerTree_totalCost_eq {c base : Real} {T : Nat → Real} (hT : UnbalancedIntegerRecurrence c base T) (n : Nat) : IntegerBranchingTree.totalCost ((unbalancedIntegerSpec c base).build n) = T n := IntegerBranchingSpec.build_totalCost_eq _ _ hT.satisfies n

Cost function computed by the generated unbalanced integer tree.

def unbalancedIntegerCost (c base : Real) (n : Nat) : Real := IntegerBranchingTree.totalCost ((unbalancedIntegerSpec c base).build n)
@[simp] theorem unbalancedIntegerCost_base {c base : Real} {n : Nat} (hn : n ≤ 2) : unbalancedIntegerCost c base n = base := by simp [unbalancedIntegerCost, unbalancedIntegerSpec, hn] theorem unbalancedIntegerCost_step {c base : Real} {n : Nat} (hn : 2 < n) : unbalancedIntegerCost c base n = unbalancedIntegerCost c base (n / 3) + unbalancedIntegerCost c base (twoThirdsCeil n) + c * (n : Real) := by rw [unbalancedIntegerCost, IntegerBranchingSpec.build_of_lt _ _ hn] simp only [IntegerBranchingTree.totalCost_node] simp [unbalancedIntegerSpec, unbalancedIntegerCost] ringtheorem unbalancedIntegerCost_satisfies (c base : Real) : UnbalancedIntegerRecurrence c base (unbalancedIntegerCost c base) := by constructor · intro n hn exact unbalancedIntegerCost_base hn · intro n hn exact unbalancedIntegerCost_step hn

At input four, the floor(4/3) child is already a leaf while the ceil(8/3) child expands once more. This witnesses genuinely unequal depth.

theorem unbalancedIntegerTree_has_unequal_depth : IntegerBranchingTree.height ((unbalancedIntegerSpec 1 1).build ((unbalancedIntegerSpec 1 1).childSize false 4)) ≠ IntegerBranchingTree.height ((unbalancedIntegerSpec 1 1).build ((unbalancedIntegerSpec 1 1).childSize true 4)) := by have hleft : (unbalancedIntegerSpec 1 1).childSize false 4 = 1 := by norm_num [unbalancedIntegerSpec] have hright : (unbalancedIntegerSpec 1 1).childSize true 4 = 3 := by norm_num [unbalancedIntegerSpec, twoThirdsCeil, Nat.ceilDiv_eq_add_pred_div] rw [hleft, hright] have hheightOne : IntegerBranchingTree.height ((unbalancedIntegerSpec 1 1).build 1) = 0 := by rw [IntegerBranchingSpec.build_of_le _ _ (by norm_num [unbalancedIntegerSpec])] rfl have hheightThree : IntegerBranchingTree.height ((unbalancedIntegerSpec 1 1).build 3) = 1 := by rw [IntegerBranchingSpec.build_of_lt _ _ (by norm_num [unbalancedIntegerSpec])] simp [IntegerBranchingTree.height, unbalancedIntegerSpec, twoThirdsCeil] omega

The limiting one-third and two-thirds branch weights sum to one.

theorem unbalancedInteger_characteristic_one : (1 : Real) / 3 + 2 / 3 = 1 := by norm_num
end Chapter04end CLRS

CLRSLean.FourthEdition.Chapter_04.Section_04_4_Recursion_Tree_Method.Branching.LevelSums

Reusable branching level sums

This file builds a full recursion tree from per-branch work-scaling ratios and proves its exact per-level cost. It also packages the convergent geometric sum bound used by the balanced textbook example.

namespace CLRSnamespace Chapter04open Finsetopen scoped BigOperatorsopen BranchingRecursionTreevariable {Branch : Type} [Fintype Branch]

A homogeneous branching expansion. A child on branch branch receives ratios branch times its parent's local work. Leaves have a common cutoff cost; this parameter is intentionally separate from the internal work scale.

def scaledBranchingTree (ratios : Branch -> Real) (rootWork leafWork : Real) : (depth : Nat) -> BranchingRecursionTree Branch depth | 0 => .leaf leafWork | depth + 1 => .node rootWork (fun branch => scaledBranchingTree ratios (rootWork * ratios branch) leafWork depth)

At level k, the total work is the root work times the kth power of the sum of the per-branch work ratios.

theorem scaledBranchingTree_levelCost (ratios : Branch -> Real) (rootWork leafWork : Real) {depth : Nat} (level : Fin depth) : levelCost (scaledBranchingTree ratios rootWork leafWork depth) level = rootWork * (∑ branch, ratios branch) ^ level.val := by induction depth generalizing rootWork with | zero => exact Fin.elim0 level | succ depth ih => refine Fin.cases ?_ (fun childLevel => ?_) level · simp [scaledBranchingTree, levelCost] · simp only [scaledBranchingTree, levelCost, Fin.cases_succ] simp_rw [ih] rw [← Finset.sum_mul, ← Finset.mul_sum] simp only [Fin.val_succ, pow_succ] ring

A full depth-d expansion has card Branch ^ d leaves.

theorem scaledBranchingTree_leafCost (ratios : Branch -> Real) (rootWork leafWork : Real) (depth : Nat) : leafCost (scaledBranchingTree ratios rootWork leafWork depth) = (Fintype.card Branch : Real) ^ depth * leafWork := by induction depth generalizing rootWork with | zero => simp [scaledBranchingTree, leafCost] | succ depth ih => simp [scaledBranchingTree, leafCost, ih, Finset.sum_const, nsmul_eq_mul, pow_succ] ring

Exact closed form of the full branching expansion through a fixed depth.

theorem scaledBranchingTree_totalCost (ratios : Branch -> Real) (rootWork leafWork : Real) (depth : Nat) : totalCost (scaledBranchingTree ratios rootWork leafWork depth) = (∑ level : Fin depth, rootWork * (∑ branch, ratios branch) ^ level.val) + (Fintype.card Branch : Real) ^ depth * leafWork := by rw [totalCost_eq_levelCosts_add_leafCost] simp_rw [scaledBranchingTree_levelCost] rw [scaledBranchingTree_leafCost]

A reusable finite geometric level-sum bound.

theorem geometricLevelSum_le (base ratio : Real) (hbase : 0 <= base) (hratio_nonneg : 0 <= ratio) (hratio_lt_one : ratio < 1) (depth : Nat) : (∑ level ∈ Finset.range depth, base * ratio ^ level) <= base / (1 - ratio) := by have hsumm : Summable fun level : Nat => ratio ^ level := summable_geometric_of_lt_one hratio_nonneg hratio_lt_one have hpartial : (∑ level ∈ Finset.range depth, ratio ^ level) <= (1 - ratio)⁻¹ := by calc (∑ level ∈ Finset.range depth, ratio ^ level) <= ∑' level : Nat, ratio ^ level := hsumm.sum_le_tsum (Finset.range depth) (fun level _ => pow_nonneg hratio_nonneg level) _ = (1 - ratio)⁻¹ := tsum_geometric_of_lt_one hratio_nonneg hratio_lt_one calc (∑ level ∈ Finset.range depth, base * ratio ^ level) = base * ∑ level ∈ Finset.range depth, ratio ^ level := by rw [Finset.mul_sum] _ <= base * (1 - ratio)⁻¹ := mul_le_mul_of_nonneg_left hpartial hbase _ = base / (1 - ratio) := by rw [div_eq_mul_inv]
end Chapter04end CLRS

CLRSLean.FourthEdition.Chapter_04.Section_04_4_Recursion_Tree_Method.Branching.Model

Full branching recursion trees

BranchingRecursionTree Branch depth is an explicit, finite expansion of a branching recurrence through exactly depth internal levels. Every internal node has one child for each value of the finite type Branch; all leaves lie at the same cutoff depth. This is the exact-power / fixed-depth model used in the textbook level-cost calculation. Floor and ceiling transfer for arbitrary input sizes is deliberately a separate concern.

namespace CLRSnamespace Chapter04open Finsetopen scoped BigOperators

A full finite branching recursion tree with depth internal levels.

inductive BranchingRecursionTree (Branch : Type) : Nat -> Type | leaf (cost : Real) : BranchingRecursionTree Branch 0 | node {depth : Nat} (work : Real) (children : Branch -> BranchingRecursionTree Branch depth) : BranchingRecursionTree Branch (depth + 1)
namespace BranchingRecursionTreevariable {Branch : Type} [Fintype Branch]

Total work stored in all internal nodes and leaves.

def totalCost : {depth : Nat} -> BranchingRecursionTree Branch depth -> Real | 0, .leaf cost => cost | _ + 1, .node work children => work + ∑ branch, totalCost (children branch)

Total contribution of the leaves at the cutoff depth.

def leafCost : {depth : Nat} -> BranchingRecursionTree Branch depth -> Real | 0, .leaf cost => cost | _ + 1, .node _ children => ∑ branch, leafCost (children branch)

Work contributed by the internal nodes at one depth.

def levelCost : {depth : Nat} -> (tree : BranchingRecursionTree Branch depth) -> Fin depth -> Real | 0, .leaf _, level => Fin.elim0 level | _ + 1, .node work children, level => Fin.cases work (fun childLevel => ∑ branch, levelCost (children branch) childLevel) level

Exact recursion-tree decomposition: total cost is the sum of all internal level costs plus the cost of the leaves at the common cutoff depth.

theorem totalCost_eq_levelCosts_add_leafCost {depth : Nat} (tree : BranchingRecursionTree Branch depth) : totalCost tree = (∑ level : Fin depth, levelCost tree level) + leafCost tree := by induction tree with | leaf cost => simp [totalCost, leafCost] | @node depth work children ih => simp only [totalCost, leafCost] rw [Fin.sum_univ_succ] simp only [levelCost, Fin.cases_zero, Fin.cases_succ] simp_rw [ih] rw [Finset.sum_add_distrib] rw [Finset.sum_comm] ring
end BranchingRecursionTreeend Chapter04end CLRS

CLRSLean.FourthEdition.Chapter_04.Section_04_4_Recursion_Tree_Method.Branching.TextbookExamples

Textbook branching-recursion-tree examples

The theorems below are exact fixed-depth expansions over real-valued problem scales. They formalize the level-cost calculations from CLRS §4.4 while keeping the assumptions visible:

  • no floor or ceiling is taken at a child size;

  • all branches are expanded through a common cutoff depth;

  • leafWork is the common cost assigned at that cutoff.

Consequently these results are the recursion-tree algebra for the original branch ratios, not an arbitrary-input termination theorem. A transfer from these exact scales to rounded natural-number recurrences needs separate monotonicity and floor/ceiling bounds.

namespace CLRSnamespace Chapter04open Finsetopen scoped BigOperatorsopen BranchingRecursionTree
The balanced example 3T(n/4) + c n^2

The fixed-depth recursion tree for 3T(n/4) + c n^2. Quadratic local work scales by 1/16 on each of the three child branches.

noncomputable def balancedThreeQuarterTree (c n leafWork : Real) (depth : Nat) : BranchingRecursionTree (Fin 3) depth := scaledBranchingTree (fun _ : Fin 3 => (1 : Real) / 16) (c * n ^ 2) leafWork depth

At level k, the balanced example costs exactly c n^2 (3/16)^k.

theorem balancedThreeQuarter_levelCost (c n leafWork : Real) {depth : Nat} (level : Fin depth) : levelCost (balancedThreeQuarterTree c n leafWork depth) level = c * n ^ 2 * ((3 : Real) / 16) ^ level.val := by rw [balancedThreeQuarterTree, scaledBranchingTree_levelCost] norm_num

Exact internal-level plus leaf decomposition for the balanced example.

theorem balancedThreeQuarter_totalCost_eq (c n leafWork : Real) (depth : Nat) : totalCost (balancedThreeQuarterTree c n leafWork depth) = (∑ level : Fin depth, c * n ^ 2 * ((3 : Real) / 16) ^ level.val) + (3 : Real) ^ depth * leafWork := by rw [balancedThreeQuarterTree, scaledBranchingTree_totalCost] norm_num

The internal work of 3T(n/4) + c n^2 is bounded by the convergent geometric sum 16/13 * c n^2; the leaf contribution remains explicit.

theorem balancedThreeQuarter_totalCost_le (c n leafWork : Real) (hc : 0 <= c) (depth : Nat) : totalCost (balancedThreeQuarterTree c n leafWork depth) <= ((16 : Real) / 13) * (c * n ^ 2) + (3 : Real) ^ depth * leafWork := by have hbase : 0 <= c * n ^ 2 := mul_nonneg hc (sq_nonneg n) have hlevels := geometricLevelSum_le (c * n ^ 2) ((3 : Real) / 16) hbase (by norm_num) (by norm_num) depth have hlevelsFin : (∑ level : Fin depth, c * n ^ 2 * ((3 : Real) / 16) ^ level.val) <= (c * n ^ 2) / (1 - (3 : Real) / 16) := by rw [Fin.sum_univ_eq_sum_range (fun level : Nat => c * n ^ 2 * ((3 : Real) / 16) ^ level) depth] exact hlevels rw [balancedThreeQuarter_totalCost_eq] calc (∑ level : Fin depth, c * n ^ 2 * ((3 : Real) / 16) ^ level.val) + (3 : Real) ^ depth * leafWork <= (c * n ^ 2) / (1 - (3 : Real) / 16) + (3 : Real) ^ depth * leafWork := add_le_add hlevelsFin le_rfl _ = ((16 : Real) / 13) * (c * n ^ 2) + (3 : Real) ^ depth * leafWork := by ring
The unbalanced example T(n/3) + T(2n/3) + c n

The two exact child-work ratios for the linear-work unbalanced recurrence.

noncomputable def thirdTwoThirdRatio : Bool -> Real | false => (1 : Real) / 3 | true => (2 : Real) / 3

A common-depth expansion of T(n/3) + T(2n/3) + c n.

The two child trees retain their different 1/3 and 2/3 scales; they are not replaced by a balanced recurrence.

noncomputable def unbalancedThirdTwoThirdTree (c n leafWork : Real) (depth : Nat) : BranchingRecursionTree Bool depth := scaledBranchingTree thirdTwoThirdRatio (c * n) leafWork depth

Since 1/3 + 2/3 = 1, every internal level in the common-depth expansion has exactly the root's linear work c n.

theorem unbalancedThirdTwoThird_levelCost (c n leafWork : Real) {depth : Nat} (level : Fin depth) : levelCost (unbalancedThirdTwoThirdTree c n leafWork depth) level = c * n := by rw [unbalancedThirdTwoThirdTree, scaledBranchingTree_levelCost] have hratio : (∑ branch : Bool, thirdTwoThirdRatio branch) = (1 : Real) := by norm_num [thirdTwoThirdRatio] rw [hratio, one_pow, mul_one]

Exact total cost through a common cutoff depth: depth * c n internal work plus one leafWork contribution for each of the 2^depth leaves.

This theorem is deliberately not advertised as an arbitrary-size solution: the actual 1/3 and 2/3 branches reach a natural-number base threshold at different depths after rounding.

theorem unbalancedThirdTwoThird_totalCost (c n leafWork : Real) (depth : Nat) : totalCost (unbalancedThirdTwoThirdTree c n leafWork depth) = (depth : Real) * (c * n) + (2 : Real) ^ depth * leafWork := by rw [unbalancedThirdTwoThirdTree, scaledBranchingTree_totalCost] have hratio : (∑ branch : Bool, thirdTwoThirdRatio branch) = (1 : Real) := by norm_num [thirdTwoThirdRatio] rw [hratio] simp [nsmul_eq_mul]
end Chapter04end CLRS
Imports
open Finsetopen scoped BigOperators

4.5. The Master Method for Solving Recurrences

This file proves the exact-power algebraic core of the Chapter 4 Master Theorem. For recurrences on exact powers, T(b^(i+1)) = a * T(b^i) + f(b^(i+1)), the normalized quantity T(b^i) / a^i unfolds into the initial value plus a finite sum of normalized forcing terms. The three Master-style exact-power criteria below then turn bounded, constant, or tail-dominated normalized forcing into the expected asymptotic conclusions.

Main result:

  • Theorem CLRS.Chapter04.h_formula: the normalized exact-power recurrence expansion.

  • Theorem CLRS.Chapter04.master_case1_geometric: bounded normalized forcing, obtained from a geometric upper bound, gives T(b^i) = Θ(a^i).

  • Theorem CLRS.Chapter04.master_case2_constant_forcing: constant normalized forcing gives T(b^i) = Θ((i+1)a^i).

  • Theorem CLRS.Chapter04.master_case2_polylog_forcing: polynomial normalized forcing c·j^k ≤ forcing ≤ C·j^k (the f(n) = Θ(n^(log_b a)·log^k n) textbook case) gives T(b^i) = Θ((i+1)^(k+1)a^i).

  • Theorem CLRS.Chapter04.master_case3_tail_dominated: a conditional normalized tail-domination criterion.

  • Theorem CLRS.Chapter04.master_case3_of_eventual_regularity: eventual forcing regularity derives T(b^i) = Θ(f(b^i)), absorbing the finite prefix into an explicitly constructed constant.

  • Theorem CLRS.Chapter04.normalized_tail_upper_of_eventual_regularity: supplies the normalized upper-bound premise of the legacy all-input wrappers.

Status: proved for these exact-power criteria, including case-3 regularity with nonnegative costs and positive forcing at the regularity threshold. The all-input transfer in Section 4.6 retains explicit monotonicity and adjacent-scale bounds; these hypotheses are not derived here.

namespace CLRSnamespace Chapter04

Exact-power recurrences

Exact-power form of the CLRS Master Theorem recurrence.

structure ExactPowerRecurrence (a b : ℕ) (f T : ℕ → ℝ) : Prop where step : ∀ i : ℕ, T (b ^ (i + 1)) = (a : ℝ) * T (b ^ i) + f (b ^ (i + 1))

The normalized value T(b^i) / a^i.

noncomputable def normalizedValue (a b : ℕ) (T : ℕ → ℝ) (i : ℕ) : ℝ := T (b ^ i) / ((a : ℝ) ^ i)

The normalized forcing term contributed at the step from i to i+1.

noncomputable def normalizedForcing (a b : ℕ) (f : ℕ → ℝ) (i : ℕ) : ℝ := f (b ^ (i + 1)) / ((a : ℝ) ^ (i + 1))

Public theorem

Unroll the exact-power recurrence after dividing by a^i.

This is the algebraic spine of the Master Theorem proof: the remaining CLRS case analysis is a question about bounding the finite sum on the right-hand side.

theorem h_formula (a b : ℕ) (f T : ℕ → ℝ) (h_rec : ExactPowerRecurrence a b f T) (ha_ne_zero : (a : ℝ) ≠ 0) (i : ℕ) : T (b ^ i) / ((a : ℝ) ^ i) = T (b ^ 0) / ((a : ℝ) ^ 0) + (∑ k ∈ range i, f (b ^ (k + 1)) / ((a : ℝ) ^ (k + 1))) := by induction' i with i ih · simp · rw [show T (b ^ (i + 1)) / ((a : ℝ) ^ (i + 1)) = T (b ^ i) / ((a : ℝ) ^ i) + f (b ^ (i + 1)) / ((a : ℝ) ^ (i + 1)) by field_simp [ha_ne_zero, pow_succ] rw [h_rec.step i] ring] rw [ih] simp [sum_range_succ, add_assoc]
lemma normalizedValue_eq_base_add_sum (a b : ℕ) (f T : ℕ → ℝ) (h_rec : ExactPowerRecurrence a b f T) (ha_ne_zero : (a : ℝ) ≠ 0) (i : ℕ) : normalizedValue a b T i = normalizedValue a b T 0 + (∑ k ∈ range i, normalizedForcing a b f k) := by simpa [normalizedValue, normalizedForcing] using h_formula a b f T h_rec ha_ne_zero iprivate theorem isBigTheta_of_eventual_bounds {f g : ℕ → ℝ} (hO : ∃ C : ℝ, 0 < C ∧ ∃ n₀ : ℕ, ∀ n, n ≥ n₀ → |f n| ≤ C * |g n|) (hΩ : ∃ c : ℝ, 0 < c ∧ ∃ n₀ : ℕ, ∀ n, n ≥ n₀ → c * |g n| ≤ |f n|) : Chapter03.isBigTheta f g := by exact ⟨(Chapter03.isBigO_iff f g).2 hO, (Chapter03.isBigOmega_iff f g).2 hΩ⟩

If the normalized recurrence values are eventually within constant multiples of a nonnegative scale s, then the original exact-power recurrence is Θ(s(i)a^i).

theorem theta_of_normalized_by_scale (a b : ℕ) (T : ℕ → ℝ) (scale : ℕ → ℝ) (ha_pos : 0 < (a : ℝ)) (hscale_nonneg : ∀ i, 0 ≤ scale i) (h_lower : ∃ c : ℝ, 0 < c ∧ ∃ n₀ : ℕ, ∀ i, i ≥ n₀ → c * scale i ≤ normalizedValue a b T i) (h_upper : ∃ C : ℝ, 0 < C ∧ ∃ n₀ : ℕ, ∀ i, i ≥ n₀ → normalizedValue a b T i ≤ C * scale i) : Chapter03.isBigTheta (fun i : ℕ => T (b ^ i)) (fun i : ℕ => scale i * ((a : ℝ) ^ i)) := by rcases h_lower with ⟨c, hc_pos, nL, hL⟩ rcases h_upper with ⟨C, hC_pos, nU, hU⟩ have ha_ne_zero : (a : ℝ) ≠ 0 := ne_of_gt ha_pos refine isBigTheta_of_eventual_bounds ?_ ?_ · refine ⟨C, hC_pos, max nL nU, ?_⟩ intro i hi have hiL : i ≥ nL := le_trans (Nat.le_max_left _ _) hi have hiU : i ≥ nU := le_trans (Nat.le_max_right _ _) hi have hscale_i := hscale_nonneg i have hnv_nonneg : 0 ≤ normalizedValue a b T i := by have hmul_nonneg : 0 ≤ c * scale i := mul_nonneg hc_pos.le hscale_i exact hmul_nonneg.trans (hL i hiL) have hT_eq : T (b ^ i) = ((a : ℝ) ^ i) * normalizedValue a b T i := by dsimp [normalizedValue] field_simp [pow_ne_zero i ha_ne_zero] have hT_abs : |T (b ^ i)| = ((a : ℝ) ^ i) * normalizedValue a b T i := by rw [hT_eq, abs_mul, abs_of_nonneg (pow_nonneg ha_pos.le i), abs_of_nonneg hnv_nonneg] have htarget_abs : |scale i * ((a : ℝ) ^ i)| = scale i * ((a : ℝ) ^ i) := by rw [abs_of_nonneg (mul_nonneg hscale_i (pow_nonneg ha_pos.le i))] calc |T (b ^ i)| = ((a : ℝ) ^ i) * normalizedValue a b T i := hT_abs _ ≤ ((a : ℝ) ^ i) * (C * scale i) := by gcongr exact hU i hiU _ = C * (scale i * ((a : ℝ) ^ i)) := by ring _ = C * |scale i * ((a : ℝ) ^ i)| := by rw [htarget_abs] · refine ⟨c, hc_pos, max nL nU, ?_⟩ intro i hi have hiL : i ≥ nL := le_trans (Nat.le_max_left _ _) hi have hscale_i := hscale_nonneg i have hnv_nonneg : 0 ≤ normalizedValue a b T i := by have hmul_nonneg : 0 ≤ c * scale i := mul_nonneg hc_pos.le hscale_i exact hmul_nonneg.trans (hL i hiL) have hT_eq : T (b ^ i) = ((a : ℝ) ^ i) * normalizedValue a b T i := by dsimp [normalizedValue] field_simp [pow_ne_zero i ha_ne_zero] have hT_abs : |T (b ^ i)| = ((a : ℝ) ^ i) * normalizedValue a b T i := by rw [hT_eq, abs_mul, abs_of_nonneg (pow_nonneg ha_pos.le i), abs_of_nonneg hnv_nonneg] have htarget_abs : |scale i * ((a : ℝ) ^ i)| = scale i * ((a : ℝ) ^ i) := by rw [abs_of_nonneg (mul_nonneg hscale_i (pow_nonneg ha_pos.le i))] calc c * |scale i * ((a : ℝ) ^ i)| = c * (scale i * ((a : ℝ) ^ i)) := by rw [htarget_abs] _ = ((a : ℝ) ^ i) * (c * scale i) := by ring _ ≤ ((a : ℝ) ^ i) * normalizedValue a b T i := by gcongr exact hL i hiL _ = |T (b ^ i)| := by rw [hT_abs]
private lemma geometric_sum_le_tsum_bound {r : ℝ} (hr_nonneg : 0 ≤ r) (hr_lt_one : r < 1) (i : ℕ) : (∑ k ∈ range i, r ^ k) ≤ (1 - r)⁻¹ := by have hsumm : Summable fun k : ℕ => r ^ k := summable_geometric_of_lt_one hr_nonneg hr_lt_one calc (∑ k ∈ range i, r ^ k) ≤ ∑' k : ℕ, r ^ k := hsumm.sum_le_tsum (range i) (fun k _ => pow_nonneg hr_nonneg k) _ = (1 - r)⁻¹ := tsum_geometric_of_lt_one hr_nonneg hr_lt_one

Master case 1, exact-power form: if the normalized forcing terms are bounded by a convergent geometric sequence, then T(b^i) = Θ(a^i).

theorem master_case1_geometric (a b : ℕ) (f T : ℕ → ℝ) (h_rec : ExactPowerRecurrence a b f T) (ha_pos : 0 < (a : ℝ)) (h_base_pos : 0 < normalizedValue a b T 0) (h_term_nonneg : ∀ k, 0 ≤ normalizedForcing a b f k) {r C : ℝ} (hr_nonneg : 0 ≤ r) (hr_lt_one : r < 1) (hC_pos : 0 < C) (h_term_upper : ∀ k, normalizedForcing a b f k ≤ C * r ^ k) : Chapter03.isBigTheta (fun i : ℕ => T (b ^ i)) (fun i : ℕ => ((a : ℝ) ^ i)) := by have ha_ne_zero : (a : ℝ) ≠ 0 := ne_of_gt ha_pos have hsum_bound : ∃ B : ℝ, 0 ≤ B ∧ ∀ i, (∑ k ∈ range i, normalizedForcing a b f k) ≤ B := by refine ⟨C * (1 - r)⁻¹, mul_nonneg hC_pos.le (inv_nonneg.mpr (sub_nonneg.mpr hr_lt_one.le)), ?_⟩ intro i calc (∑ k ∈ range i, normalizedForcing a b f k) ≤ ∑ k ∈ range i, C * r ^ k := by exact Finset.sum_le_sum (fun k _ => h_term_upper k) _ = C * (∑ k ∈ range i, r ^ k) := by simp [Finset.mul_sum] _ ≤ C * (1 - r)⁻¹ := by gcongr exact geometric_sum_le_tsum_bound hr_nonneg hr_lt_one i rcases hsum_bound with ⟨B, hB_nonneg, hB⟩ have h_lower : ∃ c : ℝ, 0 < c ∧ ∃ n₀ : ℕ, ∀ i, i ≥ n₀ → c * (fun _ : ℕ => (1 : ℝ)) i ≤ normalizedValue a b T i := by refine ⟨normalizedValue a b T 0, h_base_pos, 0, ?_⟩ intro i _hi have h_formula_i := normalizedValue_eq_base_add_sum a b f T h_rec ha_ne_zero i have hsum_nonneg : 0 ≤ ∑ k ∈ range i, normalizedForcing a b f k := Finset.sum_nonneg (fun k _ => h_term_nonneg k) calc normalizedValue a b T 0 * (1 : ℝ) = normalizedValue a b T 0 := by ring _ ≤ normalizedValue a b T i := by rw [h_formula_i] linarith have h_upper : ∃ C' : ℝ, 0 < C' ∧ ∃ n₀ : ℕ, ∀ i, i ≥ n₀ → normalizedValue a b T i ≤ C' * (fun _ : ℕ => (1 : ℝ)) i := by refine ⟨normalizedValue a b T 0 + B, by linarith, 0, ?_⟩ intro i _hi have h_formula_i := normalizedValue_eq_base_add_sum a b f T h_rec ha_ne_zero i calc normalizedValue a b T i = normalizedValue a b T 0 + (∑ k ∈ range i, normalizedForcing a b f k) := h_formula_i _ ≤ normalizedValue a b T 0 + B := by gcongr exact hB i _ = (normalizedValue a b T 0 + B) * (1 : ℝ) := by ring simpa using theta_of_normalized_by_scale a b T (fun _ : ℕ => (1 : ℝ)) ha_pos (fun _ => by norm_num) h_lower h_upper

Master case 2, exact-power form: if the normalized forcing terms are trapped between positive constants, then T(b^i) = Θ((i+1)a^i).

theorem master_case2_constant_forcing (a b : ℕ) (f T : ℕ → ℝ) (h_rec : ExactPowerRecurrence a b f T) (ha_pos : 0 < (a : ℝ)) (h_base_nonneg : 0 ≤ normalizedValue a b T 0) {c C : ℝ} (hc_pos : 0 < c) (hC_pos : 0 < C) (h_term_lower : ∀ k, c ≤ normalizedForcing a b f k) (h_term_upper : ∀ k, normalizedForcing a b f k ≤ C) : Chapter03.isBigTheta (fun i : ℕ => T (b ^ i)) (fun i : ℕ => ((i : ℝ) + 1) * ((a : ℝ) ^ i)) := by have ha_ne_zero : (a : ℝ) ≠ 0 := ne_of_gt ha_pos refine theta_of_normalized_by_scale a b T (fun i => (i : ℝ) + 1) ha_pos (fun i => by positivity) ?_ ?_ · refine ⟨c / 2, by positivity, 1, ?_⟩ intro i hi have h_formula_i := normalizedValue_eq_base_add_sum a b f T h_rec ha_ne_zero i have hsum_lower : c * (i : ℝ) ≤ ∑ k ∈ range i, normalizedForcing a b f k := by calc c * (i : ℝ) = ∑ _k ∈ range i, c := by simp [Finset.sum_const, nsmul_eq_mul, mul_comm] _ ≤ ∑ k ∈ range i, normalizedForcing a b f k := by exact Finset.sum_le_sum (fun k _ => h_term_lower k) have hi_real : 1 ≤ (i : ℝ) := by exact_mod_cast hi calc (c / 2) * ((i : ℝ) + 1) ≤ c * (i : ℝ) := by nlinarith [hc_pos] _ ≤ ∑ k ∈ range i, normalizedForcing a b f k := hsum_lower _ ≤ normalizedValue a b T i := by rw [h_formula_i] linarith · refine ⟨normalizedValue a b T 0 + C, by linarith, 0, ?_⟩ intro i _hi have h_formula_i := normalizedValue_eq_base_add_sum a b f T h_rec ha_ne_zero i have hsum_upper : (∑ k ∈ range i, normalizedForcing a b f k) ≤ C * (i : ℝ) := by calc (∑ k ∈ range i, normalizedForcing a b f k) ≤ ∑ _k ∈ range i, C := by exact Finset.sum_le_sum (fun k _ => h_term_upper k) _ = C * (i : ℝ) := by simp [Finset.sum_const, nsmul_eq_mul, mul_comm] have hi_nonneg : 0 ≤ (i : ℝ) := by positivity calc normalizedValue a b T i = normalizedValue a b T 0 + (∑ k ∈ range i, normalizedForcing a b f k) := h_formula_i _ ≤ normalizedValue a b T 0 + C * (i : ℝ) := by gcongr _ ≤ (normalizedValue a b T 0 + C) * ((i : ℝ) + 1) := by nlinarith [h_base_nonneg, hC_pos]

Case 2 with a polylog factor

The sum of the first i nonnegative k-th powers is at least (i/2)^(k+1): the terms at indices i/2, ..., i-1 each contribute at least (i/2)^k.

private lemma sum_pow_ge_half_pow (i k : ℕ) (hi : 2 ≤ i) : ((i / 2 : ℕ) : ℝ) ^ (k + 1) ≤ ∑ j ∈ Finset.range i, (j : ℝ) ^ k := by let S : Finset ℕ := Finset.Icc (i / 2) (i - 1) have hsub : S ⊆ Finset.range i := by intro j hj rw [Finset.mem_Icc] at hj rw [Finset.mem_range] omega have hsum_sub : (∑ j ∈ S, (j : ℝ) ^ k) ≤ ∑ j ∈ Finset.range i, (j : ℝ) ^ k := by exact Finset.sum_le_sum_of_subset_of_nonneg hsub (fun j _ _ => pow_nonneg (by positivity) _) have hmin : ∀ j ∈ S, ((i / 2 : ℕ) : ℝ) ^ k ≤ (j : ℝ) ^ k := by intro j hj rw [Finset.mem_Icc] at hj exact pow_le_pow_left₀ (by positivity : 0 ≤ ((i / 2 : ℕ) : ℝ)) (by exact_mod_cast hj.1) k have hcard_ge : ((i / 2 : ℕ) : ℝ) ≤ (S.card : ℝ) := by have hcard : S.card = i - i / 2 := by rw [Nat.card_Icc] omega have hge : (i / 2 : ℕ) ≤ i - i / 2 := by omega rw [hcard] exact_mod_cast hge have hsum_min : (S.card : ℝ) * ((i / 2 : ℕ) : ℝ) ^ k ≤ ∑ j ∈ S, (j : ℝ) ^ k := by calc (S.card : ℝ) * ((i / 2 : ℕ) : ℝ) ^ k = ∑ _j ∈ S, ((i / 2 : ℕ) : ℝ) ^ k := by simp [Finset.sum_const, nsmul_eq_mul] _ ≤ ∑ j ∈ S, (j : ℝ) ^ k := by exact Finset.sum_le_sum (fun j hj => hmin j hj) calc ((i / 2 : ℕ) : ℝ) ^ (k + 1) = ((i / 2 : ℕ) : ℝ) * ((i / 2 : ℕ) : ℝ) ^ k := by rw [pow_succ'] _ ≤ (S.card : ℝ) * ((i / 2 : ℕ) : ℝ) ^ k := by exact mul_le_mul_of_nonneg_right hcard_ge (by positivity) _ ≤ ∑ j ∈ S, (j : ℝ) ^ k := hsum_min _ ≤ ∑ j ∈ Finset.range i, (j : ℝ) ^ k := hsum_sub

The sum of the first i nonnegative k-th powers is Θ(i^(k+1)); this is the lower-bound half in terms of (i+1)^(k+1).

private lemma sum_pow_ge (i k : ℕ) (hi : 2 ≤ i) : (1 / 4 ^ (k + 1) : ℝ) * ((i : ℝ) + 1) ^ (k + 1) ≤ ∑ j ∈ Finset.range i, (j : ℝ) ^ k := by have hhalf := sum_pow_ge_half_pow i k hi have h4 : ((i / 2 : ℕ) : ℝ) ≥ ((i : ℝ) + 1) / 4 := by have hnat : (i / 2 : ℕ) * 4 ≥ i + 1 := by omega have hcast : ((i / 2 : ℕ) : ℝ) * 4 ≥ (i : ℝ) + 1 := by exact_mod_cast hnat linarith have hrel : (1 / 4 ^ (k + 1) : ℝ) * ((i : ℝ) + 1) ^ (k + 1) ≤ ((i / 2 : ℕ) : ℝ) ^ (k + 1) := by calc (1 / 4 ^ (k + 1) : ℝ) * ((i : ℝ) + 1) ^ (k + 1) = (((i : ℝ) + 1) / 4) ^ (k + 1) := by rw [div_pow] field_simp _ ≤ ((i / 2 : ℕ) : ℝ) ^ (k + 1) := by exact pow_le_pow_left₀ (by positivity : 0 ≤ ((i : ℝ) + 1) / 4) h4 (k + 1) exact le_trans hrel hhalf

The sum of the first i nonnegative k-th powers is at most (i+1)^(k+1): each of the i terms is at most i^k.

private lemma sum_pow_le (i k : ℕ) : (∑ j ∈ Finset.range i, (j : ℝ) ^ k) ≤ ((i : ℝ) + 1) ^ (k + 1) := by calc (∑ j ∈ Finset.range i, (j : ℝ) ^ k) ≤ ∑ _j ∈ Finset.range i, (i : ℝ) ^ k := by exact Finset.sum_le_sum (fun j hj => pow_le_pow_left₀ (by positivity) (by rw [Finset.mem_range] at hj exact_mod_cast (Nat.le_of_lt hj)) k) _ = (i : ℝ) * (i : ℝ) ^ k := by simp [Finset.sum_const, nsmul_eq_mul] _ = (i : ℝ) ^ (k + 1) := by rw [pow_succ', mul_comm] _ ≤ ((i : ℝ) + 1) ^ (k + 1) := by exact pow_le_pow_left₀ (by positivity) (by linarith) (k + 1)

Master case 2 with a polylog factor, exact-power form: if the normalized forcing terms grow polynomially, c·j^k ≤ normalizedForcing a b f j ≤ C·j^k, then T(b^i) = Θ((i+1)^(k+1)·a^i).

For f(n) = Θ(n^(log_b a)·log^k n) the normalized forcing is Θ(j^k), so this is the standard textbook extension T(n) = Θ(n^(log_b a)·log^(k+1) n); the k = 0 case recovers master_case2_constant_forcing.

theorem master_case2_polylog_forcing (a b k : ℕ) (f T : ℕ → ℝ) (h_rec : ExactPowerRecurrence a b f T) (ha_pos : 0 < (a : ℝ)) (h_base_nonneg : 0 ≤ normalizedValue a b T 0) {c C : ℝ} (hc_pos : 0 < c) (hC_pos : 0 < C) (h_term_lower : ∀ j : ℕ, c * (j : ℝ) ^ k ≤ normalizedForcing a b f j) (h_term_upper : ∀ j : ℕ, normalizedForcing a b f j ≤ C * (j : ℝ) ^ k) : Chapter03.isBigTheta (fun i : ℕ => T (b ^ i)) (fun i : ℕ => (((i : ℝ) + 1) ^ (k + 1)) * ((a : ℝ) ^ i)) := by have ha_ne_zero : (a : ℝ) ≠ 0 := ne_of_gt ha_pos refine theta_of_normalized_by_scale a b T (fun i => ((i : ℝ) + 1) ^ (k + 1)) ha_pos (fun i => by positivity) ?_ ?_ · refine ⟨c / 4 ^ (k + 1), by positivity, 2, ?_⟩ intro i hi have h_formula_i := normalizedValue_eq_base_add_sum a b f T h_rec ha_ne_zero i have hsum_lower : c * (1 / 4 ^ (k + 1) : ℝ) * ((i : ℝ) + 1) ^ (k + 1) ≤ ∑ j ∈ range i, normalizedForcing a b f j := by calc c * (1 / 4 ^ (k + 1) : ℝ) * ((i : ℝ) + 1) ^ (k + 1) ≤ c * (∑ j ∈ range i, (j : ℝ) ^ k) := by simpa [mul_assoc] using mul_le_mul_of_nonneg_left (sum_pow_ge i k hi) hc_pos.le _ = ∑ j ∈ range i, c * (j : ℝ) ^ k := by simp [Finset.mul_sum] _ ≤ ∑ j ∈ range i, normalizedForcing a b f j := by exact Finset.sum_le_sum (fun j hj => h_term_lower j) calc (c / 4 ^ (k + 1) : ℝ) * ((i : ℝ) + 1) ^ (k + 1) = c * (1 / 4 ^ (k + 1) : ℝ) * ((i : ℝ) + 1) ^ (k + 1) := by ring _ ≤ ∑ j ∈ range i, normalizedForcing a b f j := hsum_lower _ ≤ normalizedValue a b T i := by rw [h_formula_i] linarith · refine ⟨normalizedValue a b T 0 + C, by linarith, 0, ?_⟩ intro i _hi have h_formula_i := normalizedValue_eq_base_add_sum a b f T h_rec ha_ne_zero i have hsum_upper : (∑ j ∈ range i, normalizedForcing a b f j) ≤ C * ((i : ℝ) + 1) ^ (k + 1) := by calc (∑ j ∈ range i, normalizedForcing a b f j) ≤ ∑ j ∈ range i, C * (j : ℝ) ^ k := by exact Finset.sum_le_sum (fun j hj => h_term_upper j) _ = C * (∑ j ∈ range i, (j : ℝ) ^ k) := by simp [Finset.mul_sum] _ ≤ C * ((i : ℝ) + 1) ^ (k + 1) := by exact mul_le_mul_of_nonneg_left (sum_pow_le i k) hC_pos.le have hi_nonneg : 0 ≤ (i : ℝ) := by positivity have hscale_ge : (1 : ℝ) ≤ ((i : ℝ) + 1) ^ (k + 1) := by have hbase : (1 : ℝ) ≤ (i : ℝ) + 1 := by linarith have hpow := pow_le_pow_left₀ (by norm_num : (0 : ℝ) ≤ 1) hbase (k + 1) simpa using hpow calc normalizedValue a b T i = normalizedValue a b T 0 + (∑ j ∈ range i, normalizedForcing a b f j) := h_formula_i _ ≤ normalizedValue a b T 0 + C * ((i : ℝ) + 1) ^ (k + 1) := by gcongr _ ≤ (normalizedValue a b T 0 + C) * ((i : ℝ) + 1) ^ (k + 1) := by have hbase_le : normalizedValue a b T 0 ≤ normalizedValue a b T 0 * ((i : ℝ) + 1) ^ (k + 1) := by exact le_mul_of_one_le_right h_base_nonneg hscale_ge nlinarith

Master case 3, exact-power form: if the normalized recurrence value is eventually controlled by the last normalized forcing term, then the last term dominates the whole recurrence tree. This is a conditional criterion; normalized_tail_upper_of_eventual_regularity derives its upper-bound premise from forcing regularity, and master_case3_of_eventual_regularity states the resulting exact-power theorem directly in terms of f.

theorem master_case3_tail_dominated (a b : ℕ) (f T : ℕ → ℝ) (h_rec : ExactPowerRecurrence a b f T) (ha_pos : 0 < (a : ℝ)) (h_base_nonneg : 0 ≤ normalizedValue a b T 0) (h_term_nonneg : ∀ k, 0 ≤ normalizedForcing a b f k) (h_tail_upper : ∃ C : ℝ, 0 < C ∧ ∃ n₀ : ℕ, ∀ i, i ≥ n₀ → 1 ≤ i → normalizedValue a b T i ≤ C * normalizedForcing a b f (i - 1)) : Chapter03.isBigTheta (fun i : ℕ => T (b ^ i)) (fun i : ℕ => (if i = 0 then 1 else normalizedForcing a b f (i - 1)) * ((a : ℝ) ^ i)) := by have ha_ne_zero : (a : ℝ) ≠ 0 := ne_of_gt ha_pos refine theta_of_normalized_by_scale a b T (fun i : ℕ => if i = 0 then 1 else normalizedForcing a b f (i - 1)) ha_pos ?_ ?_ ?_ · intro i by_cases hi : i = 0 · simp [hi] · simp [hi, h_term_nonneg] · refine ⟨1, by norm_num, 1, ?_⟩ intro i hi have hi_pos : 0 < i := by omega have hi_ne : i ≠ 0 := Nat.ne_of_gt hi_pos have h_formula_i := normalizedValue_eq_base_add_sum a b f T h_rec ha_ne_zero i have hlast_mem : i - 1 ∈ range i := by rw [Finset.mem_range] omega have hlast_le_sum : normalizedForcing a b f (i - 1) ≤ ∑ k ∈ range i, normalizedForcing a b f k := Finset.single_le_sum (fun k _ => h_term_nonneg k) hlast_mem calc 1 * (if i = 0 then 1 else normalizedForcing a b f (i - 1)) = normalizedForcing a b f (i - 1) := by simp [hi_ne] _ ≤ ∑ k ∈ range i, normalizedForcing a b f k := hlast_le_sum _ ≤ normalizedValue a b T i := by rw [h_formula_i] linarith · rcases h_tail_upper with ⟨C, hC_pos, n₀, htail⟩ refine ⟨C, hC_pos, max 1 n₀, ?_⟩ intro i hi have hi_one : 1 ≤ i := le_trans (Nat.le_max_left _ _) hi have hi_n₀ : i ≥ n₀ := le_trans (Nat.le_max_right _ _) hi have hi_ne : i ≠ 0 := by omega calc normalizedValue a b T i ≤ C * normalizedForcing a b f (i - 1) := htail i hi_n₀ hi_one _ = C * (if i = 0 then 1 else normalizedForcing a b f (i - 1)) := by simp [hi_ne]

Case 3 from forcing regularity

Eventual forcing regularity controls the recurrence itself. The finite prefix is absorbed in the constant chosen at index i₀; no upper bound on T is assumed. Positivity at that index is needed to absorb its cost.

theorem exactPower_upper_of_eventual_regularity (a b : ℕ) (f T : ℕ → ℝ) (h_rec : ExactPowerRecurrence a b f T) (i₀ : ℕ) {c : ℝ} (hc_lt_one : c < 1) (hf_start : 0 < f (b ^ i₀)) (hf_nonneg : ∀ i, i₀ ≤ i → 0 ≤ f (b ^ i)) (h_reg : ∀ i, i₀ ≤ i → (a : ℝ) * f (b ^ i) ≤ c * f (b ^ (i + 1))) : ∃ K : ℝ, 0 < K ∧ ∀ i, i₀ ≤ i → T (b ^ i) ≤ K * f (b ^ i) := by let K : ℝ := max (T (b ^ i₀) / f (b ^ i₀)) (1 / (1 - c)) have hd : 0 < 1 - c := sub_pos.mpr hc_lt_one have hK_inv : 1 / (1 - c) ≤ K := le_max_right _ _ have hK_pos : 0 < K := lt_of_lt_of_le (div_pos zero_lt_one hd) hK_inv have hK_step : K * c + 1 ≤ K := by have := (div_le_iff₀ hd).mp hK_inv nlinarith refine ⟨K, hK_pos, ?_⟩ intro i hi induction i, hi using Nat.le_induction with | base => exact (div_le_iff₀ hf_start).mp (le_max_left _ _) | succ i hi ih => calc T (b ^ (i + 1)) = (a : ℝ) * T (b ^ i) + f (b ^ (i + 1)) := h_rec.step i _ ≤ (a : ℝ) * (K * f (b ^ i)) + f (b ^ (i + 1)) := by gcongr _ = K * ((a : ℝ) * f (b ^ i)) + f (b ^ (i + 1)) := by ring _ ≤ K * (c * f (b ^ (i + 1))) + f (b ^ (i + 1)) := by exact add_le_add (mul_le_mul_of_nonneg_left (h_reg i hi) hK_pos.le) le_rfl _ = (K * c + 1) * f (b ^ (i + 1)) := by ring _ ≤ K * f (b ^ (i + 1)) := mul_le_mul_of_nonneg_right hK_step (hf_nonneg _ (by omega))

The normalized tail-upper premise of master_case3_tail_dominated follows from eventual forcing regularity. This bridge also supplies the premise required by the existing floor/ceiling all-input wrappers.

theorem normalized_tail_upper_of_eventual_regularity (a b : ℕ) (f T : ℕ → ℝ) (h_rec : ExactPowerRecurrence a b f T) (i₀ : ℕ) {c : ℝ} (hc_lt_one : c < 1) (hf_start : 0 < f (b ^ i₀)) (hf_nonneg : ∀ i, i₀ ≤ i → 0 ≤ f (b ^ i)) (h_reg : ∀ i, i₀ ≤ i → (a : ℝ) * f (b ^ i) ≤ c * f (b ^ (i + 1))) : ∃ K : ℝ, 0 < K ∧ ∃ n₀ : ℕ, ∀ i, i ≥ n₀ → 1 ≤ i → normalizedValue a b T i ≤ K * normalizedForcing a b f (i - 1) := by obtain ⟨K, hK, hbound⟩ := exactPower_upper_of_eventual_regularity a b f T h_rec i₀ hc_lt_one hf_start hf_nonneg h_reg refine ⟨K, hK, i₀, ?_⟩ intro i hi hi_one simpa [normalizedValue, normalizedForcing, Nat.sub_add_cancel hi_one, mul_div_assoc] using div_le_div_of_nonneg_right (hbound i hi) (pow_nonneg (Nat.cast_nonneg a) i)

Master case 3 on exact powers, derived from eventual forcing regularity a f(b^i) ≤ c f(b^(i+1)), with c < 1. Nonnegative costs and one positive forcing value after the threshold suffice; arbitrary finite prefixes are allowed. The conclusion uses the original forcing function.

The usual 0 ≤ c condition may be omitted: the proof only needs c < 1 together with the stated forcing signs.

theorem master_case3_of_eventual_regularity (a b : ℕ) (f T : ℕ → ℝ) (h_rec : ExactPowerRecurrence a b f T) (i₀ : ℕ) {c : ℝ} (hc_lt_one : c < 1) (h_base_nonneg : 0 ≤ T 1) (hf_start : 0 < f (b ^ i₀)) (hf_nonneg : ∀ i, 0 ≤ f (b ^ i)) (h_reg : ∀ i, i₀ ≤ i → (a : ℝ) * f (b ^ i) ≤ c * f (b ^ (i + 1))) : Chapter03.isBigTheta (fun i : ℕ => T (b ^ i)) (fun i : ℕ => f (b ^ i)) := by have hT_nonneg : ∀ i, 0 ≤ T (b ^ i) := by intro i induction i with | zero => simpa using h_base_nonneg | succ i ih => rw [h_rec.step i] exact add_nonneg (mul_nonneg (Nat.cast_nonneg a) ih) (hf_nonneg _) obtain ⟨K, hK, hbound⟩ := exactPower_upper_of_eventual_regularity a b f T h_rec i₀ hc_lt_one hf_start (fun i _ => hf_nonneg i) h_reg refine isBigTheta_of_eventual_bounds ⟨K, hK, i₀, ?_⟩ ⟨1, zero_lt_one, 1, ?_⟩ · intro i hi simpa [abs_of_nonneg (hT_nonneg i), abs_of_nonneg (hf_nonneg i)] using hbound i hi · intro i hi obtain ⟨j, rfl⟩ := Nat.exists_eq_succ_of_ne_zero (by omega : i ≠ 0) rw [abs_of_nonneg (hT_nonneg _), abs_of_nonneg (hf_nonneg _), one_mul, h_rec.step j] exact le_add_of_nonneg_left (mul_nonneg (Nat.cast_nonneg a) (hT_nonneg j))
end Chapter04end CLRS
Imports
open Finsetopen scoped BigOperators

4.6. Proof of the Continuous Master Theorem

The continuous master theorem (CLRS §4.6) treats the divide-and-conquer recurrence T(n) = a T(n/b) + f(n) with exact division n/b. Unrolling the recurrence to the bottom of the recursion tree expresses the work as the geometric sum

  T(b^k) = Θ(1) + Σ_{j = 0}^{k - 1} a^j · f(b^(k - j))

whose three textbook cases are governed by the ratio r = a / b^p when the forcing is polynomial f(n) = n^p. This section formalizes the analytic core — the real geometric series — and the three continuous cases, then bridges the continuous scale back to the discrete comparison scales used by the all-input Master-theorem wrappers.

Main results:

  • Definition geomSum: the real geometric partial sum Σ_{j<k} r^j.

  • Theorem geomSum_le_of_lt_one: a ratio 0 ≤ r < 1 yields a partial sum bounded independently of the number of terms.

  • Theorem geomSum_eq_of_one: a ratio r = 1 yields exactly k terms.

  • Theorem geomSum_bigTheta_of_gt_one: a ratio r > 1 yields a geometric tail Θ(r^k).

  • Definition continuousWork: the recursion-tree work Σ_{j<k} a^j (b^(k-j))^p with polynomial forcing.

  • Theorem continuousWork_eq_geomSum: the work equals b^(p·k) · geomSum (a / b^p) k.

  • Theorems continuous_master_case1, continuous_master_case2, and continuous_master_case3: the three continuous cases.

  • Theorem continuous_case1_scale_eq_criticalPowerScale: the continuous case-1 scale equals the discrete critical-power scale on exact powers.

Status: proved for the continuous geometric-series core and its bridge to the discrete comparison scales. Floors, ceilings, and the non-polynomial forcing of the full all-input transfer remain in CLRSLean.Chapter_04.Section_04_6_Master_Theorem_All_Input.

Notation conventions used in this section:

  • a : number of subproblems

  • b : factor by which the subproblem size shrinks

  • p : exponent of the polynomial forcing f(n) = n^p

  • k : continuous level count (so n = b^k)

namespace CLRSnamespace Chapter04

The real geometric series

The real geometric partial sum Σ_{j ∈ range k} r^j.

noncomputable def geomSum (r : ℝ) (k : ℕ) : ℝ := ∑ j ∈ range k, r ^ j

A geometric partial sum with a nonnegative ratio is nonnegative.

theorem geomSum_nonneg {r : ℝ} (hr0 : 0 ≤ r) (k : ℕ) : 0 ≤ geomSum r k := by rw [geomSum] exact Finset.sum_nonneg (by intro j hj; exact pow_nonneg hr0 j)

Continuous master theorem, case-1 ratio. For a ratio 0 ≤ r < 1 the partial sum is bounded by (1 - r)⁻¹, independently of the number of terms. This is the analytic reason the forcing in the third Master case (whose ratio is smaller than one) contributes only Θ(f(n)) rather than growing with the recursion tree.

theorem geomSum_le_of_lt_one {r : ℝ} (hr0 : 0 ≤ r) (hr1 : r < 1) (k : ℕ) : geomSum r k ≤ (1 - r)⁻¹ := by have hr_ne : r ≠ 1 := by linarith have hpos : 0 < 1 - r := by linarith rw [geomSum, geom_sum_eq hr_ne] have hrk_nonneg : 0 ≤ r ^ k := pow_nonneg hr0 _ have hnum : 1 - r ^ k ≤ 1 := by linarith have hdiv : (1 - r ^ k) / (1 - r) ≤ 1 / (1 - r) := (div_le_div_iff_of_pos_right hpos).mpr hnum have hcast : (r ^ k - 1) / (r - 1) = (1 - r ^ k) / (1 - r) := by field_simp [ne_of_gt hpos] ring rw [hcast] rw [inv_eq_one_div] exact hdiv

Continuous master theorem, case-2 ratio. For a ratio r = 1 the partial sum has exactly k unit terms, the logarithmic factor in the second Master case.

theorem geomSum_eq_of_one (k : ℕ) : geomSum 1 k = (k : ℝ) := by rw [geomSum] simp only [one_pow, Finset.sum_const, Finset.card_range, nsmul_eq_mul, mul_one]

For a ratio r > 1 the partial sum is at most a constant times its last term r^k.

theorem geomSum_le_geometric_of_gt_one {r : ℝ} (hr1 : 1 < r) (k : ℕ) : geomSum r k ≤ (r - 1)⁻¹ * r ^ k := by have hr_ne : r ≠ 1 := by linarith rw [geomSum, geom_sum_eq hr_ne] calc (r ^ k - 1) / (r - 1) ≤ r ^ k / (r - 1) := (div_le_div_iff_of_pos_right (sub_pos.mpr hr1)).mpr (by linarith) _ = (r - 1)⁻¹ * r ^ k := by rw [div_eq_mul_inv] ring

For a ratio r > 1 and a nonempty partial sum, the sum is at least a constant times its last term r^k.

theorem geometric_le_geomSum_of_gt_one {r : ℝ} (hr1 : 1 < r) {k : ℕ} (hk : 1 ≤ k) : (r : ℝ)⁻¹ * r ^ k ≤ geomSum r k := by have hr_pos : 0 < r := lt_trans zero_lt_one hr1 have hlast : r ^ (k - 1) ≤ geomSum r k := by rw [geomSum] exact Finset.single_le_sum (by intro j hj; exact pow_nonneg hr_pos.le j) (by rw [mem_range]; omega) have hfac : (r : ℝ)⁻¹ * r ^ k = r ^ (k - 1) := by field_simp [ne_of_gt hr_pos] rw [mul_comm, ← pow_succ] rw [show (k - 1) + 1 = k by omega] rw [hfac] exact hlast

Continuous master theorem, case-3 ratio. For a ratio r > 1 the partial sum is Θ(r^k), dominated by its largest (last) term. This is the analytic reason the forcing in the first Master case (whose ratio exceeds one) grows to Θ(n^(log_b a)).

theorem geomSum_bigTheta_of_gt_one {r : ℝ} (hr1 : 1 < r) : Chapter03.isBigTheta (geomSum r) (fun k => r ^ k) := by have hr_pos : 0 < r := lt_trans zero_lt_one hr1 constructor · rw [Chapter03.isBigO_iff] refine ⟨(r - 1)⁻¹, inv_pos.mpr (sub_pos.mpr hr1), 0, ?_⟩ intro k hk rw [abs_of_nonneg (geomSum_nonneg hr_pos.le k)] rw [abs_of_nonneg (pow_nonneg hr_pos.le k)] exact geomSum_le_geometric_of_gt_one hr1 k · rw [Chapter03.isBigOmega_iff] refine ⟨(r : ℝ)⁻¹, inv_pos.mpr hr_pos, 1, ?_⟩ intro k hk rw [abs_of_nonneg (geomSum_nonneg hr_pos.le k)] rw [abs_of_nonneg (pow_nonneg hr_pos.le k)] exact geometric_le_geomSum_of_gt_one hr1 hk

The continuous recursion-tree work

The recursion-tree work of a divide-and-conquer recurrence with polynomial forcing f(n) = n^p: level j has a^j subproblems, each of size b^(k-j) and cost (b^(k-j))^p (here n = b^k is an exact power, so division n/b is exact).

noncomputable def continuousWork (a b p k : ℕ) : ℝ := ∑ j ∈ range k, (a : ℝ) ^ j * ((b : ℝ) ^ (k - j)) ^ p

The continuous ratio a / b^p that governs the geometric series.

noncomputable def continuousRatio (a b p : ℕ) : ℝ := (a : ℝ) / (b : ℝ) ^ p

The recursion-tree work is nonnegative.

theorem continuousWork_nonneg (a b p k : ℕ) : 0 ≤ continuousWork a b p k := by rw [continuousWork] exact Finset.sum_nonneg (by intro j hj exact mul_nonneg (pow_nonneg (Nat.cast_nonneg a) j) (pow_nonneg (pow_nonneg (Nat.cast_nonneg b) (k - j)) p))

The recursion-tree work factors as b^(p·k) times the geometric series with ratio a / b^p: each level's work a^j (b^(k-j))^p equals b^(p·k) (a / b^p)^j.

theorem continuousWork_eq_geomSum (a b p : ℕ) (hb : (b : ℝ) ≠ 0) (k : ℕ) : continuousWork a b p k = (b : ℝ) ^ (p * k) * geomSum (continuousRatio a b p) k := by unfold continuousWork geomSum continuousRatio rw [Finset.mul_sum] apply Finset.sum_congr rfl intro j hj have hjlt : j < k := by simpa [mem_range] using hj calc (a : ℝ) ^ j * ((b : ℝ) ^ (k - j)) ^ p = (a : ℝ) ^ j * (b : ℝ) ^ ((k - j) * p) := by rw [← pow_mul] _ = (a : ℝ) ^ j * (b : ℝ) ^ (p * (k - j)) := by rw [Nat.mul_comm (k - j) p] _ = (a : ℝ) ^ j * (b : ℝ) ^ (p * k - p * j) := by rw [show p * (k - j) = p * k - p * j by exact Nat.mul_sub_left_distrib p k j] _ = (a : ℝ) ^ j * ((b : ℝ) ^ (p * k) / (b : ℝ) ^ (p * j)) := by rw [pow_sub₀ (b : ℝ) hb (Nat.mul_le_mul_left p (le_of_lt hjlt))] rw [div_eq_mul_inv] _ = (b : ℝ) ^ (p * k) * ((a : ℝ) ^ j / (b : ℝ) ^ (p * j)) := by ring _ = (b : ℝ) ^ (p * k) * ((a : ℝ) / (b : ℝ) ^ p) ^ j := by rw [div_pow, ← pow_mul]

The three continuous cases

Continuous master theorem, case 1. When the ratio a / b^p > 1 (so p < log_b a, i.e. the forcing n^p is polynomially smaller than the critical n^(log_b a)), the recursion-tree work is Θ(a^k), matching Θ(n^(log_b a)) on the exact power n = b^k.

theorem continuous_master_case1 (a b p : ℕ) (hb : (b : ℝ) ≠ 0) (hr : 1 < continuousRatio a b p) : Chapter03.isBigTheta (continuousWork a b p) (fun k => (a : ℝ) ^ k) := by have hratio : 1 < (a : ℝ) / (b : ℝ) ^ p := by simpa [continuousRatio] using hr have hpow_identity : ∀ k, (b : ℝ) ^ (p * k) * ((a : ℝ) / (b : ℝ) ^ p) ^ k = (a : ℝ) ^ k := by intro k calc (b : ℝ) ^ (p * k) * ((a : ℝ) / (b : ℝ) ^ p) ^ k = (b : ℝ) ^ (p * k) * ((a : ℝ) ^ k / (b : ℝ) ^ (p * k)) := by rw [div_pow, ← pow_mul] _ = (a : ℝ) ^ k := by field_simp [pow_ne_zero (p * k) hb] constructor · rw [Chapter03.isBigO_iff] refine ⟨((a : ℝ) / (b : ℝ) ^ p - 1)⁻¹, inv_pos.mpr (sub_pos.mpr hratio), 0, ?_⟩ intro k hk rw [abs_of_nonneg (continuousWork_nonneg a b p k)] rw [abs_of_nonneg (pow_nonneg (Nat.cast_nonneg a) k)] rw [continuousWork_eq_geomSum a b p hb k] have hgeom : geomSum ((a : ℝ) / (b : ℝ) ^ p) k ≤ ((a : ℝ) / (b : ℝ) ^ p - 1)⁻¹ * ((a : ℝ) / (b : ℝ) ^ p) ^ k := geomSum_le_geometric_of_gt_one hratio k have hterm : (b : ℝ) ^ (p * k) * geomSum ((a : ℝ) / (b : ℝ) ^ p) k ≤ ((a : ℝ) / (b : ℝ) ^ p - 1)⁻¹ * (a : ℝ) ^ k := by calc (b : ℝ) ^ (p * k) * geomSum ((a : ℝ) / (b : ℝ) ^ p) k ≤ (b : ℝ) ^ (p * k) * (((a : ℝ) / (b : ℝ) ^ p - 1)⁻¹ * ((a : ℝ) / (b : ℝ) ^ p) ^ k) := mul_le_mul_of_nonneg_left hgeom (pow_nonneg (Nat.cast_nonneg b) (p * k)) _ = ((a : ℝ) / (b : ℝ) ^ p - 1)⁻¹ * ((b : ℝ) ^ (p * k) * ((a : ℝ) / (b : ℝ) ^ p) ^ k) := by ring _ = ((a : ℝ) / (b : ℝ) ^ p - 1)⁻¹ * (a : ℝ) ^ k := by rw [hpow_identity k] exact hterm · rw [Chapter03.isBigOmega_iff] refine ⟨((a : ℝ) / (b : ℝ) ^ p)⁻¹, inv_pos.mpr (lt_trans zero_lt_one hratio), 1, ?_⟩ intro k hk rw [abs_of_nonneg (continuousWork_nonneg a b p k)] rw [abs_of_nonneg (pow_nonneg (Nat.cast_nonneg a) k)] rw [continuousWork_eq_geomSum a b p hb k] have hgeom : ((a : ℝ) / (b : ℝ) ^ p)⁻¹ * ((a : ℝ) / (b : ℝ) ^ p) ^ k ≤ geomSum ((a : ℝ) / (b : ℝ) ^ p) k := geometric_le_geomSum_of_gt_one hratio hk have hterm : ((a : ℝ) / (b : ℝ) ^ p)⁻¹ * (a : ℝ) ^ k ≤ (b : ℝ) ^ (p * k) * geomSum ((a : ℝ) / (b : ℝ) ^ p) k := by calc ((a : ℝ) / (b : ℝ) ^ p)⁻¹ * (a : ℝ) ^ k = ((a : ℝ) / (b : ℝ) ^ p)⁻¹ * ((b : ℝ) ^ (p * k) * ((a : ℝ) / (b : ℝ) ^ p) ^ k) := by rw [hpow_identity k] _ = (b : ℝ) ^ (p * k) * (((a : ℝ) / (b : ℝ) ^ p)⁻¹ * ((a : ℝ) / (b : ℝ) ^ p) ^ k) := by ring _ ≤ (b : ℝ) ^ (p * k) * geomSum ((a : ℝ) / (b : ℝ) ^ p) k := mul_le_mul_of_nonneg_left hgeom (pow_nonneg (Nat.cast_nonneg b) (p * k)) exact hterm

Continuous master theorem, case 2. When the ratio a / b^p = 1 (so a = b^p, i.e. the forcing n^p matches the critical n^(log_b a)), the recursion-tree work is Θ(k · a^k), the logarithmic case.

theorem continuous_master_case2 (a b p : ℕ) (hb : (b : ℝ) ≠ 0) (hr : continuousRatio a b p = 1) : Chapter03.isBigTheta (continuousWork a b p) (fun k => (k : ℝ) * (a : ℝ) ^ k) := by have hratio : (a : ℝ) / (b : ℝ) ^ p = 1 := by simpa [continuousRatio] using hr have hbp : (b : ℝ) ^ p ≠ 0 := pow_ne_zero p hb have hbase : (b : ℝ) ^ p = (a : ℝ) := by field_simp [hbp] at hratio exact hratio.symm have hpow_identity : ∀ k, (b : ℝ) ^ (p * k) = (a : ℝ) ^ k := by intro k rw [← hbase] rw [← pow_mul] constructor · rw [Chapter03.isBigO_iff] refine ⟨1, by norm_num, 0, ?_⟩ intro k hk rw [abs_of_nonneg (continuousWork_nonneg a b p k)] rw [abs_of_nonneg (mul_nonneg (Nat.cast_nonneg k) (pow_nonneg (Nat.cast_nonneg a) k))] rw [continuousWork_eq_geomSum a b p hb k] rw [continuousRatio, hratio, geomSum_eq_of_one k] rw [hpow_identity k] nlinarith · rw [Chapter03.isBigOmega_iff] refine ⟨1, by norm_num, 0, ?_⟩ intro k hk rw [abs_of_nonneg (continuousWork_nonneg a b p k)] rw [abs_of_nonneg (mul_nonneg (Nat.cast_nonneg k) (pow_nonneg (Nat.cast_nonneg a) k))] rw [continuousWork_eq_geomSum a b p hb k] rw [continuousRatio, hratio, geomSum_eq_of_one k] rw [hpow_identity k] nlinarith

Continuous master theorem, case 3. When the ratio a / b^p < 1 (so p > log_b a, i.e. the forcing n^p dominates the critical n^(log_b a)), the recursion-tree work is Θ(b^(p·k)), matching Θ(f(n)) = Θ(n^p) on the exact power n = b^k.

theorem continuous_master_case3 (a b p : ℕ) (hb : (b : ℝ) ≠ 0) (hr0 : 0 ≤ continuousRatio a b p) (hr1 : continuousRatio a b p < 1) : Chapter03.isBigTheta (continuousWork a b p) (fun k => (b : ℝ) ^ (p * k)) := by have hratio0 : 0 ≤ (a : ℝ) / (b : ℝ) ^ p := by simpa [continuousRatio] using hr0 have hratio1 : (a : ℝ) / (b : ℝ) ^ p < 1 := by simpa [continuousRatio] using hr1 constructor · rw [Chapter03.isBigO_iff] refine ⟨(1 - (a : ℝ) / (b : ℝ) ^ p)⁻¹, inv_pos.mpr (sub_pos.mpr hratio1), 0, ?_⟩ intro k hk rw [abs_of_nonneg (continuousWork_nonneg a b p k)] rw [abs_of_nonneg (pow_nonneg (Nat.cast_nonneg b) (p * k))] rw [continuousWork_eq_geomSum a b p hb k] rw [continuousRatio] have hgeom : geomSum ((a : ℝ) / (b : ℝ) ^ p) k ≤ (1 - (a : ℝ) / (b : ℝ) ^ p)⁻¹ := geomSum_le_of_lt_one hratio0 hratio1 k have hle := mul_le_mul_of_nonneg_left hgeom (pow_nonneg (Nat.cast_nonneg b) (p * k)) simpa [mul_comm, mul_left_comm, mul_assoc] using hle · rw [Chapter03.isBigOmega_iff] refine ⟨1, by norm_num, 1, ?_⟩ intro k hk have hk1 : 1 ≤ k := hk rw [abs_of_nonneg (continuousWork_nonneg a b p k)] rw [abs_of_nonneg (pow_nonneg (Nat.cast_nonneg b) (p * k))] rw [continuousWork_eq_geomSum a b p hb k] rw [continuousRatio] have hgeom : 1 ≤ geomSum ((a : ℝ) / (b : ℝ) ^ p) k := by rw [geomSum] rw [show (1 : ℝ) = ((a : ℝ) / (b : ℝ) ^ p) ^ 0 by simp] exact Finset.single_le_sum (f := fun j => ((a : ℝ) / (b : ℝ) ^ p) ^ j) (by intro j hj; exact pow_nonneg hratio0 j) (a := 0) (by rw [mem_range]; exact lt_of_lt_of_le zero_lt_one hk1) simpa [mul_comm, mul_left_comm, mul_assoc] using (mul_le_mul_of_nonneg_left hgeom (pow_nonneg (Nat.cast_nonneg b) (p * k)))

Bridge to the discrete comparison scales

The continuous case-1 scale a^k at the exact power n = b^k is exactly the discrete critical-power scale CLRS.­Chapter04.­criticalPowerScale, so the continuous master theorem's Θ(a^k) conclusion is the Θ(n^(log_b a)) scale of the all-input wrappers (through CLRS.­Chapter04.­criticalPowerScale_isBigTheta_realLogScale).

theorem continuous_case1_scale_eq_criticalPowerScale (a b : ℕ) (hb : 1 < b) (k : ℕ) : (a : ℝ) ^ k = criticalPowerScale a b (b ^ k) := by unfold criticalPowerScale rw [Nat.log_pow hb]

The discrete case-2 scale CLRS.­Chapter04.­criticalPowerLogScale on the exact power n = b^k is exactly (k + 1) · a^k, matching the continuous case-2 scale k · a^k up to the leading + 1.

theorem continuous_case2_criticalPowerLogScale_eq (a b : ℕ) (hb : 1 < b) (k : ℕ) : criticalPowerLogScale a b (b ^ k) = ((k : ℝ) + 1) * (a : ℝ) ^ k := by unfold criticalPowerLogScale rw [continuous_case1_scale_eq_criticalPowerScale a b hb k] rw [Nat.log_pow hb]
end Chapter04end CLRS

Definitions and proofs

CLRSLean.Chapter_04.Section_04_6_Master_Theorem_All_Input

Discrete critical-power scale for the exact-power Master theorem case 1. On exact powers it satisfies criticalPowerScale a b (b^i) = a^i; between exact powers it is the step function determined by Nat.log b n.

This, criticalPowerLogScale, and tailDominatedScale are deliberately weaker and cleaner than the analytic scales n^(log_b a), but it is enough to make the exact-power-to-all-input bridge concrete for the three Master cases.

def criticalPowerScale (a b : ℕ) (n : ℕ) : ℝ := (a : ℝ) ^ Nat.log b n

Discrete case-2 Master scale. On exact powers this is (i+1) a^i; between exact powers it is the step function determined by Nat.log b n.

def criticalPowerLogScale (a b : ℕ) (n : ℕ) : ℝ := ((Nat.log b n : ℝ) + 1) * criticalPowerScale a b n

When 1 ≤ a and 1 < b, the discrete critical-power scale a^(⌊log_b n⌋) is asymptotically equivalent to the real-log scale n^(log_b a).

This is the main bridge between the discrete all-input Master-theorem proof and the standard CLRS statement in terms of n^(log_b a). The constant factor is at most a in the Ω direction and 1 in the O direction, so the asymptotic class is exact.

Try this: [apply] ring_nf The `ring` tactic failed to close the goal. Use `ring_nf` to obtain a normal form. Note that `ring` works primarily in *commutative* rings. If you have a noncommutative ring, abelian group or module, consider using `noncomm_ring`, `abel` or `module` instead.Try this: [apply] ring_nf The `ring` tactic failed to close the goal. Use `ring_nf` to obtain a normal form. Note that `ring` works primarily in *commutative* rings. If you have a noncommutative ring, abelian group or module, consider using `noncomm_ring`, `abel` or `module` instead.Try this: [apply] ring_nf The `ring` tactic failed to close the goal. Use `ring_nf` to obtain a normal form. Note that `ring` works primarily in *commutative* rings. If you have a noncommutative ring, abelian group or module, consider using `noncomm_ring`, `abel` or `module` instead.Try this: [apply] ring_nf The `ring` tactic failed to close the goal. Use `ring_nf` to obtain a normal form. Note that `ring` works primarily in *commutative* rings. If you have a noncommutative ring, abelian group or module, consider using `noncomm_ring`, `abel` or `module` instead.Try this: [apply] ring_nf The `ring` tactic failed to close the goal. Use `ring_nf` to obtain a normal form. Note that `ring` works primarily in *commutative* rings. If you have a noncommutative ring, abelian group or module, consider using `noncomm_ring`, `abel` or `module` instead.Try this: [apply] ring_nf The `ring` tactic failed to close the goal. Use `ring_nf` to obtain a normal form. Note that `ring` works primarily in *commutative* rings. If you have a noncommutative ring, abelian group or module, consider using `noncomm_ring`, `abel` or `module` instead.Try this: [apply] ring_nf The `ring` tactic failed to close the goal. Use `ring_nf` to obtain a normal form. Note that `ring` works primarily in *commutative* rings. If you have a noncommutative ring, abelian group or module, consider using `noncomm_ring`, `abel` or `module` instead.Try this: [apply] ring_nf The `ring` tactic failed to close the goal. Use `ring_nf` to obtain a normal form. Note that `ring` works primarily in *commutative* rings. If you have a noncommutative ring, abelian group or module, consider using `noncomm_ring`, `abel` or `module` instead. theorem criticalPowerScale_isBigTheta_realLogScale (a b : ℕ) (ha : 1 ≤ a) (hb : 1 < b) : Chapter03.isBigTheta (criticalPowerScale a b) (realLogScale a b) := by have ha1 : 1 ≤ (a : ℝ) := by exact_mod_cast ha have ha_pos : 0 < (a : ℝ) := by exact lt_of_lt_of_le (by norm_num : (0 : ℝ) < 1) ha1 have ha_nonneg : 0 ≤ (a : ℝ) := ha_pos.le have hb1 : 1 < (b : ℝ) := by exact_mod_cast hb have hb_pos : 0 < (b : ℝ) := by exact_mod_cast Nat.lt_trans Nat.zero_lt_one hb have hb_nonneg : 0 ≤ (b : ℝ) := hb_pos.le have hb_log_pos : 0 < Real.log (b : ℝ) := Real.log_pos hb1 have hb_log_ne_zero : Real.log (b : ℝ) ≠ 0 := ne_of_gt hb_log_pos have ha_log_nonneg : 0 ≤ Real.log (a : ℝ) := Real.log_nonneg ha1 -- The exponent α = log_b a have hα_nonneg : 0 ≤ realLogExponent a b := by dsimp [realLogExponent] exact div_nonneg ha_log_nonneg hb_log_pos.le -- Key identity: b^α = a have h_base_identity : (b : ℝ) ^ (realLogExponent a b) = (a : ℝ) := by dsimp [realLogExponent] calc (b : ℝ) ^ (Real.log (a : ℝ) / Real.log (b : ℝ)) = Real.exp (Real.log (b : ℝ) * (Real.log (a : ℝ) / Real.log (b : ℝ))) := by rw [Real.rpow_def_of_pos hb_pos] _ = Real.exp (Real.log (a : ℝ)) := by field_simp [hb_log_ne_zero] _ = (a : ℝ) := Real.exp_log ha_pos -- formula for criticalPowerScale as a real power have hcrit_formula (n : ℕ) : (criticalPowerScale a b n : ℝ) = (a : ℝ) ^ (Nat.log b n : ℝ) := by dsimp [criticalPowerScale] simp [Real.rpow_natCast] constructor · -- O direction: criticalPowerScale a b = O(realLogScale a b) refine (Chapter03.isBigO_iff _ _).mpr ?_ refine ⟨1, by norm_num, 1, ?_⟩ intro n hn have hn_ne_zero : n ≠ 0 := by omega set k := Nat.log b n with hk_def have hpow_le : (b : ℕ) ^ k ≤ n := Nat.pow_log_le_self b hn_ne_zero have hpow_le_real : ((b : ℕ) ^ k : ℝ) ≤ (n : ℝ) := by exact_mod_cast hpow_le have hn_nonneg : 0 ≤ (n : ℝ) := by exact_mod_cast Nat.zero_le n have hcrit_nonneg : 0 ≤ criticalPowerScale a b n := by rw [hcrit_formula n] exact Real.rpow_nonneg (by exact_mod_cast Nat.zero_le a) _ have hreal_nonneg : 0 ≤ realLogScale a b n := by dsimp [realLogScale] apply Real.rpow_nonneg hn_nonneg set α := realLogExponent a b with hα_def -- Core identity: a^k = (b^k)^α have hlog_mul : Real.log (b : ℝ) * α = Real.log (a : ℝ) := by dsimp [α, realLogExponent] field_simp [hb_log_ne_zero] have h_key : (a : ℝ) ^ (k : ℝ) = ((b : ℝ) ^ (k : ℝ)) ^ α := by calc (a : ℝ) ^ (k : ℝ) = Real.exp (Real.log (a : ℝ) * (k : ℝ)) := by rw [Real.rpow_def_of_pos ha_pos] _ = Real.exp ((k : ℝ) * Real.log (a : ℝ)) := by Try this: [apply] ring_nf The `ring` tactic failed to close the goal. Use `ring_nf` to obtain a normal form. Note that `ring` works primarily in *commutative* rings. If you have a noncommutative ring, abelian group or module, consider using `noncomm_ring`, `abel` or `module` instead.ring _ = Real.exp ((k : ℝ) * (Real.log (b : ℝ) * α)) := by rw [hlog_mul] _ = Real.exp ((Real.log (b : ℝ) * α) * (k : ℝ)) := by Try this: [apply] ring_nf The `ring` tactic failed to close the goal. Use `ring_nf` to obtain a normal form. Note that `ring` works primarily in *commutative* rings. If you have a noncommutative ring, abelian group or module, consider using `noncomm_ring`, `abel` or `module` instead.ring _ = Real.exp (Real.log (b : ℝ) * (α * (k : ℝ))) := by Try this: [apply] ring_nf The `ring` tactic failed to close the goal. Use `ring_nf` to obtain a normal form. Note that `ring` works primarily in *commutative* rings. If you have a noncommutative ring, abelian group or module, consider using `noncomm_ring`, `abel` or `module` instead.ring _ = Real.exp (Real.log (b : ℝ) * ((k : ℝ) * α)) := by Try this: [apply] ring_nf The `ring` tactic failed to close the goal. Use `ring_nf` to obtain a normal form. Note that `ring` works primarily in *commutative* rings. If you have a noncommutative ring, abelian group or module, consider using `noncomm_ring`, `abel` or `module` instead.ring _ = (b : ℝ) ^ ((k : ℝ) * α) := by rw [Real.rpow_def_of_pos hb_pos] _ = ((b : ℝ) ^ (k : ℝ)) ^ α := by rw [Real.rpow_mul hb_nonneg (k : ℝ) α] have hbpow_nonneg : 0 ≤ (b : ℝ) ^ (k : ℝ) := Real.rpow_nonneg hb_nonneg _ have hbpow_le_n : (b : ℝ) ^ (k : ℝ) ≤ (n : ℝ) := by rw [Real.rpow_natCast] simpa [Nat.cast_pow] using hpow_le_real calc |criticalPowerScale a b n| = criticalPowerScale a b n := abs_of_nonneg hcrit_nonneg _ = (a : ℝ) ^ (k : ℝ) := by rw [hcrit_formula n, hk_def] _ = ((b : ℝ) ^ (k : ℝ)) ^ α := by rw [h_key] _ ≤ (n : ℝ) ^ α := Real.rpow_le_rpow hbpow_nonneg hbpow_le_n hα_nonneg _ = realLogScale a b n := rfl _ = |realLogScale a b n| := by rw [abs_of_nonneg hreal_nonneg] _ = 1 * |realLogScale a b n| := by ring · -- Ω direction: realLogScale a b = O(criticalPowerScale a b) have ha_inv_pos : 0 < (a : ℝ)⁻¹ := inv_pos.mpr ha_pos refine (Chapter03.isBigOmega_iff _ _).mpr ?_ refine ⟨(a : ℝ)⁻¹, ha_inv_pos, 1, ?_⟩ intro n hn have hn_ne_zero : n ≠ 0 := by omega set k := Nat.log b n with hk_def have hpow_lt : n < b ^ (k + 1) := Nat.lt_pow_succ_log_self hb n have hcrit_nonneg : 0 ≤ criticalPowerScale a b n := by rw [hcrit_formula n] exact Real.rpow_nonneg (by exact_mod_cast Nat.zero_le a) _ have hn_nonneg : 0 ≤ (n : ℝ) := by exact_mod_cast Nat.zero_le n have hreal_nonneg : 0 ≤ realLogScale a b n := by dsimp [realLogScale] apply Real.rpow_nonneg hn_nonneg set α := realLogExponent a b with hα_def have hlog_mul : Real.log (b : ℝ) * α = Real.log (a : ℝ) := by dsimp [α, realLogExponent] field_simp [hb_log_ne_zero] have h_key : (a : ℝ) ^ (k : ℝ) = ((b : ℝ) ^ (k : ℝ)) ^ α := by calc (a : ℝ) ^ (k : ℝ) = Real.exp (Real.log (a : ℝ) * (k : ℝ)) := by rw [Real.rpow_def_of_pos ha_pos] _ = Real.exp ((k : ℝ) * Real.log (a : ℝ)) := by Try this: [apply] ring_nf The `ring` tactic failed to close the goal. Use `ring_nf` to obtain a normal form. Note that `ring` works primarily in *commutative* rings. If you have a noncommutative ring, abelian group or module, consider using `noncomm_ring`, `abel` or `module` instead.ring _ = Real.exp ((k : ℝ) * (Real.log (b : ℝ) * α)) := by rw [hlog_mul] _ = Real.exp ((Real.log (b : ℝ) * α) * (k : ℝ)) := by Try this: [apply] ring_nf The `ring` tactic failed to close the goal. Use `ring_nf` to obtain a normal form. Note that `ring` works primarily in *commutative* rings. If you have a noncommutative ring, abelian group or module, consider using `noncomm_ring`, `abel` or `module` instead.ring _ = Real.exp (Real.log (b : ℝ) * (α * (k : ℝ))) := by Try this: [apply] ring_nf The `ring` tactic failed to close the goal. Use `ring_nf` to obtain a normal form. Note that `ring` works primarily in *commutative* rings. If you have a noncommutative ring, abelian group or module, consider using `noncomm_ring`, `abel` or `module` instead.ring _ = Real.exp (Real.log (b : ℝ) * ((k : ℝ) * α)) := by Try this: [apply] ring_nf The `ring` tactic failed to close the goal. Use `ring_nf` to obtain a normal form. Note that `ring` works primarily in *commutative* rings. If you have a noncommutative ring, abelian group or module, consider using `noncomm_ring`, `abel` or `module` instead.ring _ = (b : ℝ) ^ ((k : ℝ) * α) := by rw [Real.rpow_def_of_pos hb_pos] _ = ((b : ℝ) ^ (k : ℝ)) ^ α := by rw [Real.rpow_mul hb_nonneg (k : ℝ) α] have hbpow_nonneg : 0 ≤ (b : ℝ) ^ (k : ℝ) := Real.rpow_nonneg hb_nonneg _ -- n < b^(k+1) in ℕ → (n:ℝ) ≤ (b:ℝ) * (b:ℝ)^(k:ℝ) in ℝ have h_n_le_b_mul_bpow : (n : ℝ) ≤ (b : ℝ) * (b : ℝ) ^ (k : ℝ) := by have h_n_lt_bpow_succ_nat : n < b ^ (k + 1) := hpow_lt have h_n_lt_bpow_succ_real : (n : ℝ) < ((b : ℕ) ^ (k + 1) : ℝ) := by exact_mod_cast h_n_lt_bpow_succ_nat have h_bpow_succ_eq : ((b : ℕ) ^ (k + 1) : ℝ) = (b : ℝ) * (b : ℝ) ^ (k : ℝ) := by simp [pow_succ, Real.rpow_natCast, mul_comm] linarith -- n^α ≤ (b * b^k)^α = b^α * (b^k)^α = a * a^k = a * criticalPowerScale have h_bound : realLogScale a b n ≤ (a : ℝ) * (criticalPowerScale a b n : ℝ) := by calc realLogScale a b n = (n : ℝ) ^ α := rfl _ ≤ ((b : ℝ) * (b : ℝ) ^ (k : ℝ)) ^ α := Real.rpow_le_rpow hn_nonneg h_n_le_b_mul_bpow hα_nonneg _ = ((b : ℝ) ^ α) * (((b : ℝ) ^ (k : ℝ)) ^ α) := by rw [Real.mul_rpow (z := α) hb_nonneg hbpow_nonneg] _ = (a : ℝ) * (((b : ℝ) ^ (k : ℝ)) ^ α) := by rw [h_base_identity] _ = (a : ℝ) * ((a : ℝ) ^ (k : ℝ)) := by rw [h_key] _ = (a : ℝ) * (criticalPowerScale a b n : ℝ) := by rw [hcrit_formula n, hk_def] calc (a : ℝ)⁻¹ * |realLogScale a b n| = (a : ℝ)⁻¹ * realLogScale a b n := by rw [abs_of_nonneg hreal_nonneg] _ ≤ (a : ℝ)⁻¹ * ((a : ℝ) * (criticalPowerScale a b n : ℝ)) := by gcongr _ = criticalPowerScale a b n := by field_simp [ne_of_gt ha_pos] _ = |criticalPowerScale a b n| := by rw [abs_of_nonneg hcrit_nonneg]
Imports
open scoped BigOperators

4.7. Akra–Bazzi Recurrences

The Akra–Bazzi method (CLRS §4.7) solves divide-and-conquer recurrences of the form

  T(n) = Σ_i a_i · T(n / b_i) + g(n)

whose recursion tree is not uniformly branching. The method first finds the unique exponent p solving the root equation Σ_i a_i b_i^(-p) = 1, then bounds T(n) by Θ(n^p (1 + ∫₁ⁿ g(u) / u^(p+1) du)).

This section formalizes the recurrence hypotheses, the root equation and its scale n^p, proves that a single branch recovers the continuous master theorem of §4.6 (root p = log_b a), proves the multi-branch root is unique and nonnegative, proves the fundamental scale-invariance bridge Σᵢ aᵢ (n/bᵢ)^p = n^p, records the classic two-branch instance T(n) = T(n/3) + T(2n/3) + n whose root is p = 1, and lays out the integral asymptotic form with its explicit polynomial-smoothness predicate.

Main results:

  • Definition charTerm / charFun: the characteristic term a / b^p and the characteristic function Σ a_i / b_i^p.

  • Definition IsAkraBazziRoot: the root equation Σ a_i / b_i^p = 1.

  • Theorem akraBazziRoot_single: a single branch (a, b) has root p = log_b a, the continuous master-theorem exponent.

  • Theorem akraBazziRoot_single_unique: the root is unique.

  • Theorem akraBazziRoot_two_thirds_one: the two-branch instance T(n) = T(n/3) + T(2n/3) has root p = 1.

  • Theorem akraBazzi_single_branch_corollary: the single-branch root and its scale n^p coincide with the discrete CLRS.­Chapter04.­realLogScale used by the continuous master theorem.

  • Theorem akraBazziRoot_unique: the multi-branch root is unique.

  • Theorem akraBazziRoot_nonneg: the multi-branch root is nonnegative.

  • Theorem akraBazzi_root_scale_invariance: the fundamental multi-branch bridge Σᵢ aᵢ (n/bᵢ)^p = n^p.

  • Definition akraBazziIntegral / akraBazziScale: the discrete integral Σ_{u=1}^n g(u)/u^(p+1) and the scale n^p (1 + Σ_{u≤n} g(u)/u^(p+1)).

  • Definition PolynomialGrowth: the explicit polynomial-smoothness predicate c n^q ≤ g n ≤ C n^q with monotonicity and nonnegativity.

  • Definition SatisfiesAkraBazzi: the recurrence T(n) = Σᵢ aᵢ T(⌊n/bᵢ⌋) + g(n) with floor perturbation and a constant base case.

  • Theorems akraBazziIntegral_mono, akraBazziIntegral_sub, akraBazziIntegral_lower_const, and akraBazziIntegral_bounded_of_lt: the integral's monotonicity, its tail decomposition, its positive lower bound, and its boundedness for q < p (the convergent p-series).

  • Theorems akraBazziIntegral_tail_lower and akraBazzi_increment_lower: the integral tail is at least its number of terms times its smallest term, and the single-branch increment a (n/b)^p (I n - I ⌊n/b⌋) dominates g n by a positive factor — the analytic core of the upper-bound substitution proof.

  • Definition akraBazziIncrement: the per-branch increment a (n/b)^p (I n - I ⌊n/b⌋).

  • Theorems akraBazziIntegral_tail_upper, akraBazzi_increment_upper_single, akraBazzi_increment_upper, and akraBazzi_increment_lower_multi: the multi-branch increment is bounded above and below by a positive constant times g n.

  • Theorem akraBazzi_scale_decomp: the children's scales sum to the scale minus the total increment.

  • Theorem akraBazzi_T_nonneg: a solution is nonnegative.

  • Theorem akraBazzi_T_ge_g: above the base threshold, g n ≤ T n.

  • Theorems akraBazzi_upper_bound_nonneg and akraBazzi_upper_bound: the solution is O(n^p (1 + I n)), the upper recurrence-to-integral comparison.

  • Theorem akraBazzi_lower_bound: the solution is Ω(n^p (1 + I n)) when p + 1 ≤ q, the lower comparison in the forcing-dominated regime.

  • Theorem akraBazzi_lower_bound_critical: the solution is Ω(n^p (1 + I n)) when q = p, the lower comparison in the critical regime, established by the substitution induction with the power floor loss absorbed by the driving term.

  • Theorem akraBazzi_bigTheta_critical: the critical upper and lower comparisons packaged as the textbook Θ(n^p (1 + I n)) conclusion.

  • Theorem akraBazzi_bigTheta: the full Θ(n^p (1 + I n)) bound when p + 1 ≤ q.

  • Definition akraBazziSmoothingFn: the smoothing function ε x = 1 / √x, whose drop dominates the one-step floor loss.

  • Lemma akraBazzi_smoothing_scale_floor_ge: the floored smoothing scale ⌊n/b⌋^p (1 + ε⌊n/b⌋) is eventually a subsolution of the homogeneous recurrence — the discrete analogue of the Kuszmaul–Leiserson smoothing step.

  • Theorem akraBazzi_lower_bound_leaf: the solution is Ω(n^p (1 + I n)) when 0 ≤ q < p, the lower comparison in the deep leaf-dominated regime, closed by the smoothing factor.

  • Theorem akraBazzi_bigTheta_leaf: the full Θ(n^p (1 + I n)) bound when 0 ≤ q < p.

Status: proved for the root equation, the single-branch corollary (which recovers the master theorem), the multi-branch root uniqueness/nonnegativity, the scale-invariance bridge, the integral machinery, the two-sided increment bounds, and the recurrence-to-integral comparison in both directions — the upper bound T(n) = O(n^p(1+I n)) for arbitrary p ≥ 0, q ≥ 0, and the matching lower bound T(n) = Ω(n^p(1+I n)) (hence T(n) = Θ(n^p(1+I n))) in the forcing-dominated regime p + 1 ≤ q, the critical regime q = p, and the deep leaf-dominated regime 0 ≤ q < p (established by the smoothing-function argument). The companion module Section_04_7_Akra_Bazzi.Generalized closes the remaining exponent regimes, including p = 0 and p < q < p + 1, under the same PolynomialGrowth predicate. Its public akraBazzi_bigTheta_nonneg theorem is exported by the chapter guide. The predicate requires monotonicity and a positive two-sided monomial sandwich; children are explicit floors, not unrestricted perturbations.

Notation conventions used in this section:

  • aᵢ : the number of subproblems of branch i

  • bᵢ : the size divisor of branch i (a real > 1)

  • p : the Akra–Bazzi root exponent

  • g : the driving (additive) function

namespace CLRSnamespace Chapter04

The Akra–Bazzi hypotheses

A branch (aᵢ, bᵢ) of an Akra–Bazzi recurrence: aᵢ subproblems, each of size n / bᵢ, with aᵢ ≥ 1 and bᵢ > 1.

structure AkraBazziBranch where a : ℕ b : ℝ ha_pos : 0 < a hb_gt_one : 1 < b

An Akra–Bazzi recurrence. branches is the nonempty list of branches, g is the driving function, and T is the solution. The recurrence is stated on exact powers of a common base; the floor/ceiling and polynomial-smoothness refinements of the full CLRS statement are left to the gap note.

structure AkraBazziRecurrence where branches : List AkraBazziBranch g : ℕ → ℝ T : ℕ → ℝ hnonempty : branches ≠ []

The root equation

The characteristic term a / b^p of one branch.

noncomputable def charTerm (a : ℕ) (b : ℝ) (p : ℝ) : ℝ := (a : ℝ) / (b : ℝ) ^ p

The characteristic function Σ_i a_i / b_i^p.

noncomputable def charFun (branches : List (ℕ × ℝ)) (p : ℝ) : ℝ := (branches.map (fun ab => charTerm ab.1 ab.2 p)).sum

The Akra–Bazzi root equation Σ_i a_i / b_i^p = 1.

noncomputable def IsAkraBazziRoot (branches : List (ℕ × ℝ)) (p : ℝ) : Prop := charFun branches p = 1

The real base-power identity b^(log_b a) = a for natural a ≥ 1 and b > 1. This is the bridge from the Akra–Bazzi root equation to the master-theorem exponent.

theorem rpow_realLogExponent (a b : ℕ) (ha : 1 ≤ a) (hb : 1 < b) : (b : ℝ) ^ realLogExponent a b = (a : ℝ) := by have hb_pos : 0 < (b : ℝ) := by exact_mod_cast (lt_trans (by norm_num : (0:ℕ) < 1) hb) have hb_log_pos : 0 < Real.log (b : ℝ) := Real.log_pos (by exact_mod_cast hb) have hb_log_ne : Real.log (b : ℝ) ≠ 0 := ne_of_gt hb_log_pos have ha_pos : 0 < (a : ℝ) := by exact_mod_cast (lt_of_lt_of_le (by norm_num : (0:ℕ) < 1) ha) unfold realLogExponent calc (b : ℝ) ^ (Real.log (a : ℝ) / Real.log (b : ℝ)) = Real.exp (Real.log (b : ℝ) * (Real.log (a : ℝ) / Real.log (b : ℝ))) := by rw [Real.rpow_def_of_pos hb_pos] _ = Real.exp (Real.log (a : ℝ)) := by field_simp [hb_log_ne] _ = (a : ℝ) := Real.exp_log ha_pos

Akra–Bazzi root, single branch. The recurrence T(n) = a T(n/b) + g(n) has root p = log_b a, the master-theorem exponent. This is the Akra–Bazzi root equation a / b^p = 1 for one branch.

theorem akraBazziRoot_single (a b : ℕ) (ha : 1 ≤ a) (hb : 1 < b) : IsAkraBazziRoot [(a, (b : ℝ))] (realLogExponent a b) := by unfold IsAkraBazziRoot charFun charTerm simp [List.sum] rw [rpow_realLogExponent a b ha hb] exact div_self (by exact_mod_cast (show (a : ℕ) ≠ 0 by omega))

The single-branch root is unique: two roots p and q must agree. This is the monotonicity of p ↦ a / b^p in a single branch.

theorem akraBazziRoot_single_unique {a b : ℕ} (ha : 1 ≤ a) (hb : 1 < b) {p q : ℝ} (hp : IsAkraBazziRoot [(a, (b : ℝ))] p) (hq : IsAkraBazziRoot [(a, (b : ℝ))] q) : p = q := by have hb_pos : 0 < (b : ℝ) := by exact_mod_cast (lt_trans (by norm_num : (0:ℕ) < 1) hb) have ha_pos : 0 < (a : ℝ) := by exact_mod_cast (lt_of_lt_of_le (by norm_num : (0:ℕ) < 1) ha) have hbp : (b : ℝ) ^ p = (a : ℝ) := by unfold IsAkraBazziRoot charFun charTerm at hp simp [List.sum] at hp field_simp [ne_of_gt (Real.rpow_pos_of_pos hb_pos p)] at hp exact hp.symm have hbq : (b : ℝ) ^ q = (a : ℝ) := by unfold IsAkraBazziRoot charFun charTerm at hq simp [List.sum] at hq field_simp [ne_of_gt (Real.rpow_pos_of_pos hb_pos q)] at hq exact hq.symm have hlog_eq : p * Real.log (b : ℝ) = q * Real.log (b : ℝ) := by calc p * Real.log (b : ℝ) = Real.log ((b : ℝ) ^ p) := by rw [Real.log_rpow hb_pos] _ = Real.log ((b : ℝ) ^ q) := by rw [hbp, hbq] _ = q * Real.log (b : ℝ) := by rw [Real.log_rpow hb_pos] have hb_log_ne : Real.log (b : ℝ) ≠ 0 := ne_of_gt (Real.log_pos (by exact_mod_cast hb)) exact mul_right_cancel₀ hb_log_ne hlog_eq

The two-branch instance

Akra–Bazzi root, classic instance. The two-branch recurrence T(n) = T(n/3) + T(2n/3) + n has root p = 1, since 3^(-1) + (3/2)^(-1) = 1/3 + 2/3 = 1.

theorem akraBazziRoot_two_thirds_one : IsAkraBazziRoot [(1, (3 : ℝ)), (1, (3 : ℝ) / 2)] 1 := by unfold IsAkraBazziRoot charFun charTerm norm_num [List.sum]

The single-branch corollary

Akra–Bazzi single-branch corollary. For a single branch (a, b) the root is p = log_b a, and its scale n^p is exactly the discrete CLRS.­Chapter04.­realLogScale used by the continuous master theorem (§4.6). Hence the Akra–Bazzi method recovers the master-theorem exponent and Θ(n^(log_b a)) bound for polynomial forcing (through CLRS.­Chapter04.­continuous_master_case1, CLRS.­Chapter04.­continuous_master_case2, and CLRS.­Chapter04.­continuous_master_case3).

theorem akraBazzi_single_branch_corollary (a b : ℕ) (ha : 1 ≤ a) (hb : 1 < b) : IsAkraBazziRoot [(a, (b : ℝ))] (realLogExponent a b) ∧ (∀ n : ℕ, (n : ℝ) ^ realLogExponent a b = realLogScale a b n) := by constructor · exact akraBazziRoot_single a b ha hb · intro n rfl

The multi-branch root: monotonicity, uniqueness, and scale invariance

A branch (aᵢ, bᵢ) is valid when 0 < aᵢ and 1 < bᵢ.

def BranchValid (ab : ℕ × ℝ) : Prop := 0 < ab.1 ∧ 1 < ab.2

Every branch in the list is BranchValid.

def BranchesValid (branches : List (ℕ × ℝ)) : Prop := ∀ ab ∈ branches, BranchValid ab

The characteristic term a / b^p is strictly decreasing in p for a valid branch: raising the exponent p shrinks b^(-p) (since b > 1), hence shrinks the whole term a · b^(-p).

lemma charTerm_lt_of_lt {a : ℕ} {b p q : ℝ} (ha : 0 < a) (hb : 1 < b) (hpq : p < q) : charTerm a b q < charTerm a b p := by unfold charTerm have hb_pos : 0 < b := lt_trans (by norm_num : (0 : ℝ) < 1) hb have hbpq : b ^ p < b ^ q := Real.rpow_lt_rpow_of_exponent_lt hb hpq have hbinv : (b ^ q)⁻¹ < (b ^ p)⁻¹ := inv_strictAntiOn (Real.rpow_pos_of_pos hb_pos p) (Real.rpow_pos_of_pos hb_pos q) hbpq have ha_pos : 0 < (a : ℝ) := by exact_mod_cast ha simpa [div_eq_mul_inv] using (mul_lt_mul_of_pos_left hbinv ha_pos : (a : ℝ) * (b ^ q)⁻¹ < (a : ℝ) * (b ^ p)⁻¹)

The characteristic function Σ aᵢ / bᵢ^p is strictly decreasing in p for a nonempty valid branch list. Every term shrinks, so the sum shrinks.

theorem charFun_lt_of_lt {branches : List (ℕ × ℝ)} (hvalid : BranchesValid branches) (hnonempty : branches ≠ []) {p q : ℝ} (hpq : p < q) : charFun branches q < charFun branches p := by rcases branches with _ | ⟨ab, rest⟩ · contradiction · have hvalid_ab : BranchValid ab := hvalid ab (by simp) unfold charFun simp only [List.map_cons, List.sum_cons] have hhead : charTerm ab.1 ab.2 q < charTerm ab.1 ab.2 p := charTerm_lt_of_lt hvalid_ab.1 hvalid_ab.2 hpq have hrest : (rest.map (fun x => charTerm x.1 x.2 q)).sum ≤ (rest.map (fun x => charTerm x.1 x.2 p)).sum := by apply List.sum_le_sum intro x hx have hvalid_x : BranchValid x := hvalid x (by simp [hx]) exact le_of_lt (charTerm_lt_of_lt hvalid_x.1 hvalid_x.2 hpq) -- head + rest_q < head_p + rest_p exact add_lt_add_of_lt_of_le hhead hrest

Akra–Bazzi root uniqueness (multi-branch). The root equation Σ aᵢ / bᵢ^p = 1 has at most one real solution, because charFun is strictly decreasing. This generalizes akraBazziRoot_single_unique from a single branch to any nonempty valid branch list.

theorem akraBazziRoot_unique {branches : List (ℕ × ℝ)} (hvalid : BranchesValid branches) (hnonempty : branches ≠ []) {p q : ℝ} (hp : IsAkraBazziRoot branches p) (hq : IsAkraBazziRoot branches q) : p = q := by by_contra hne rcases lt_or_gt_of_ne hne with hpq | hqp · have hlt : charFun branches q < charFun branches p := charFun_lt_of_lt hvalid hnonempty hpq rw [hp, hq] at hlt exact (lt_irrefl _ hlt).elim · have hlt : charFun branches p < charFun branches q := charFun_lt_of_lt hvalid hnonempty hqp rw [hp, hq] at hlt exact (lt_irrefl _ hlt).elim

Akra–Bazzi root is nonnegative. Since charFun 0 = Σ aᵢ ≥ 1 and charFun is strictly decreasing down to 1, the root satisfies p ≥ 0.

theorem akraBazziRoot_nonneg {branches : List (ℕ × ℝ)} (hvalid : BranchesValid branches) (hnonempty : branches ≠ []) {p : ℝ} (hp : IsAkraBazziRoot branches p) : 0 ≤ p := by by_contra hpneg have hp_lt : p < 0 := lt_of_not_ge hpneg have hchar : charFun branches 0 < charFun branches p := charFun_lt_of_lt hvalid hnonempty hp_lt have hchar0_ge : 1 ≤ charFun branches 0 := by rcases branches with _ | ⟨ab, rest⟩ · contradiction · have hvalid_ab : BranchValid ab := hvalid ab (by simp) unfold charFun charTerm simp only [List.map_cons, List.sum_cons, Real.rpow_zero, div_one] have hab_ge : 1 ≤ (ab.1 : ℝ) := by exact_mod_cast (Nat.succ_le_iff.mpr hvalid_ab.1) have hrest_nonneg : 0 ≤ (rest.map (fun x : ℕ × ℝ => (x.1 : ℝ))).sum := by apply List.sum_nonneg intro y hy rw [List.mem_map] at hy rcases hy with ⟨x, _hx, rfl⟩ exact Nat.cast_nonneg x.1 linarith rw [hp] at hchar linarith

Akra–Bazzi scale invariance (multi-branch). For a root p, the p-weight of one level of the recursion tree is preserved: Σᵢ aᵢ (n / bᵢ)^p = n^p. This is the fundamental bridge from the root equation Σ aᵢ / bᵢ^p = 1 to the scale n^p that governs every level, and it is the multi-branch generalization of akraBazziRoot_single.

theorem akraBazzi_root_scale_invariance (branches : List (ℕ × ℝ)) (p : ℝ) (hvalid : BranchesValid branches) (hroot : IsAkraBazziRoot branches p) (n : ℕ) : (branches.map (fun ab => (ab.1 : ℝ) * ((n : ℝ) / ab.2) ^ p)).sum = (n : ℝ) ^ p := by have hn_nonneg : 0 ≤ (n : ℝ) := by positivity have hmap : branches.map (fun ab => (ab.1 : ℝ) * ((n : ℝ) / ab.2) ^ p) = branches.map (fun ab => (n : ℝ) ^ p * ((ab.1 : ℝ) / ab.2 ^ p)) := by rw [List.map_eq_map_iff] intro ab hab have hb_nonneg : 0 ≤ ab.2 := le_of_lt (lt_trans (by norm_num : (0 : ℝ) < 1) (hvalid ab hab).2) rw [Real.div_rpow hn_nonneg hb_nonneg] ring rw [hmap] rw [List.sum_map_mul_left] change (n : ℝ) ^ p * charFun branches p = (n : ℝ) ^ p rw [hroot] ring

The integral asymptotic form

The Akra–Bazzi integral: the discrete form of ∫₁ⁿ g(u) / u^(p+1) du, summed over u = 1, …, n. This is the sum the recursion tree accumulates.

noncomputable def akraBazziIntegral (p : ℝ) (g : ℕ → ℝ) (n : ℕ) : ℝ := ∑ u ∈ Finset.range n, g (u + 1) / ((u + 1 : ℕ) : ℝ) ^ (p + 1)

The Akra–Bazzi scale: the discrete form of the textbook n^p (1 + ∫₁ⁿ g(u) / u^(p+1) du).

noncomputable def akraBazziScale (p : ℝ) (g : ℕ → ℝ) (n : ℕ) : ℝ := (n : ℝ) ^ p * (1 + akraBazziIntegral p g n)

The per-branch integral increment at size n: a (n/b)^p (I n - I ⌊n/b⌋) — the amount of scale n^p "lost" by rounding the subproblem size down from n/b to ⌊n/b⌋.

noncomputable def akraBazziIncrement (p : ℝ) (g : ℕ → ℝ) (ab : ℕ × ℝ) (n : ℕ) : ℝ := (ab.1 : ℝ) * ((n : ℝ) / ab.2) ^ p * (akraBazziIntegral p g n - akraBazziIntegral p g (⌊(n : ℝ) / ab.2⌋₊))

Polynomial growth (smoothness) of the driving function. The forcing function g is nonnegative, nondecreasing, and sandwiched between two positive monomials of a common exponent q: c n^q ≤ g n ≤ C n^q. This is the discrete, total-function analogue of CLRS's polynomially bounded driving function, and it is the regularity assumption under which the recursion tree compares to the integral scale.

def PolynomialGrowth (g : ℕ → ℝ) (q : ℝ) : Prop := (∀ n : ℕ, 0 ≤ g n) ∧ (∀ {m n : ℕ}, m ≤ n → g m ≤ g n) ∧ ∃ c C : ℝ, 0 < c ∧ 0 < C ∧ (∀ n : ℕ, 1 ≤ n → c * (n : ℝ) ^ q ≤ g n) ∧ (∀ n : ℕ, 1 ≤ n → g n ≤ C * (n : ℝ) ^ q)

The recurrence T(n) = Σᵢ aᵢ T(⌊n/bᵢ⌋) + g(n) with a constant base case. The floor ⌊n/bᵢ⌋ is the perturbation of CLRS §4.7: the argument is rounded down to the nearest integer subproblem size. T is 0 at 0, 1 for 1 ≤ n ≤ n₀, and satisfies the recurrence above the base-case threshold n₀.

def SatisfiesAkraBazzi (branches : List (ℕ × ℝ)) (g T : ℕ → ℝ) (n₀ : ℕ) : Prop := T 0 = 0 ∧ (∀ n, 1 ≤ n → n ≤ n₀ → T n = 1) ∧ (∀ n, n₀ < n → T n = (branches.map (fun ab => (ab.1 : ℝ) * T (⌊(n : ℝ) / ab.2⌋₊))).sum + g n)

Recurrence-to-integral comparison

The floor-perturbed subproblem size is strictly smaller than its parent.

lemma floor_div_lt_self {b : ℝ} (hb : 1 < b) {n : ℕ} (hn : 0 < n) : ⌊(n : ℝ) / b⌋₊ < n := by have hb_pos : 0 < b := lt_trans (by norm_num : (0 : ℝ) < 1) hb have hn_pos : 0 < (n : ℝ) := by exact_mod_cast hn have hdiv_lt : (n : ℝ) / b < (n : ℝ) := by rw [div_lt_iff₀ hb_pos] simpa using (mul_lt_mul_of_pos_left hb hn_pos : (n : ℝ) * 1 < (n : ℝ) * b) have hfl : (⌊(n : ℝ) / b⌋₊ : ℝ) ≤ (n : ℝ) / b := Nat.floor_le (le_of_lt (div_pos hn_pos hb_pos)) exact_mod_cast (lt_of_le_of_lt hfl hdiv_lt)

The Akra–Bazzi integral is nonnegative.

lemma akraBazziIntegral_nonneg {p : ℝ} {g : ℕ → ℝ} (hg : ∀ n, 0 ≤ g n) (n : ℕ) : 0 ≤ akraBazziIntegral p g n := by unfold akraBazziIntegral apply Finset.sum_nonneg intro u _hu exact div_nonneg (hg (u + 1)) (Real.rpow_nonneg (by positivity) (p + 1))

The Akra–Bazzi integral is monotone in its upper limit.

lemma akraBazziIntegral_mono {p : ℝ} {g : ℕ → ℝ} (hg : ∀ n, 0 ≤ g n) {m n : ℕ} (hmn : m ≤ n) : akraBazziIntegral p g m ≤ akraBazziIntegral p g n := by unfold akraBazziIntegral rw [← Nat.add_sub_of_le hmn] rw [Finset.sum_range_add (fun u => g (u + 1) / ((u + 1 : ℕ) : ℝ) ^ (p + 1)) m (n - m)] exact le_add_of_nonneg_right (Finset.sum_nonneg (by intro x _hx exact div_nonneg (hg (m + x + 1)) (Real.rpow_nonneg (by positivity) (p + 1))))

The integral increment I n - I m is the tail sum over u ∈ (m, n].

lemma akraBazziIntegral_sub {p : ℝ} {g : ℕ → ℝ} {m n : ℕ} (hmn : m ≤ n) : akraBazziIntegral p g n - akraBazziIntegral p g m = ∑ u ∈ Finset.range (n - m), g (m + u + 1) / ((m + u + 1 : ℕ) : ℝ) ^ (p + 1) := by unfold akraBazziIntegral let f : ℕ → ℝ := fun u => g (u + 1) / ((u + 1 : ℕ) : ℝ) ^ (p + 1) have hsum : ∑ u ∈ Finset.range n, f u = ∑ u ∈ Finset.range m, f u + ∑ u ∈ Finset.range (n - m), f (m + u) := by conv_lhs => rw [← Nat.add_sub_of_le hmn] rw [Finset.sum_range_add f m (n - m)] rw [hsum] rw [add_sub_cancel_left]

The integral is bounded below by a positive constant (the first term).

lemma akraBazziIntegral_lower_const {p q : ℝ} {g : ℕ → ℝ} (hsmooth : PolynomialGrowth g q) : ∃ c : ℝ, 0 < c ∧ ∀ n, 1 ≤ n → c ≤ akraBazziIntegral p g n := by rcases hsmooth with ⟨hgnonneg, _hgmono, c, _C, hcpos, _hCpos, hglower, _hgupper⟩ refine ⟨c, hcpos, ?_⟩ intro n hn unfold akraBazziIntegral have hterm : c ≤ g 1 / (1 : ℝ) ^ (p + 1) := by have hg1 := hglower 1 (by norm_num : 1 ≤ 1) simpa using hg1 have hterm_le_sum : g 1 / (1 : ℝ) ^ (p + 1) ≤ ∑ u ∈ Finset.range n, g (u + 1) / ((u + 1 : ℕ) : ℝ) ^ (p + 1) := by have h0mem : 0 ∈ Finset.range n := by rw [Finset.mem_range]; exact hn have := Finset.single_le_sum (f := fun u => g (u + 1) / ((u + 1 : ℕ) : ℝ) ^ (p + 1)) (by intro u _hu; exact div_nonneg (hgnonneg (u + 1)) (Real.rpow_nonneg (by positivity) (p + 1))) h0mem simpa using this exact le_trans hterm hterm_le_sum

The Akra–Bazzi scale is nonnegative at every input.

lemma akraBazziScale_nonneg {p : ℝ} {g : ℕ → ℝ} (Variable name `hp` is not explicitly referenced. The binding can be removed (if unused) or named `_` (if used implicitly). Note: This linter can be disabled with `set_option linter.unusedVariables false`hp : 0 ≤ p) (hg : ∀ n, 0 ≤ g n) (n : ℕ) : 0 ≤ akraBazziScale p g n := by unfold akraBazziScale have hn : 0 ≤ (n : ℝ) ^ p := Real.rpow_nonneg (by positivity) p have hI : 0 ≤ akraBazziIntegral p g n := akraBazziIntegral_nonneg hg n positivity

The integral is bounded for q < p: the forcing g ≤ C n^q gives a convergent p-series Σ u^(q-p-1).

lemma akraBazziIntegral_bounded_of_lt {p q : ℝ} {g : ℕ → ℝ} (hsmooth : PolynomialGrowth g q) (hqp : q < p) : ∃ C : ℝ, ∀ n, akraBazziIntegral p g n ≤ C := by rcases hsmooth with ⟨_hgnonneg, _hgmono, _c, Cg, _hcpos, hCpos, _hglower, hgupper⟩ have hδ : 1 < p - q + 1 := by linarith have hsum : Summable (fun v : ℕ => (v : ℝ) ^ (q - p - 1)) := by have h0 : Summable (fun n : ℕ => ((n : ℝ) ^ (p - q + 1))⁻¹) := (Real.summable_nat_rpow_inv (p := p - q + 1)).mpr hδ refine h0.congr ?_ intro n rw [← Real.rpow_neg (by positivity : 0 ≤ (n : ℝ)) (p - q + 1)] congr 1 ring refine ⟨Cg * (∑' v : ℕ, (v : ℝ) ^ (q - p - 1)), ?_⟩ intro n unfold akraBazziIntegral calc (∑ u ∈ Finset.range n, g (u + 1) / ((u + 1 : ℕ) : ℝ) ^ (p + 1)) ≤ ∑ u ∈ Finset.range n, Cg * ((u + 1 : ℕ) : ℝ) ^ (q - p - 1) := by apply Finset.sum_le_sum intro u _hu have hu1 : 1 ≤ u + 1 := by omega have hg : g (u + 1) ≤ Cg * ((u + 1 : ℕ) : ℝ) ^ q := hgupper (u + 1) hu1 calc g (u + 1) / ((u + 1 : ℕ) : ℝ) ^ (p + 1) ≤ Cg * ((u + 1 : ℕ) : ℝ) ^ q / ((u + 1 : ℕ) : ℝ) ^ (p + 1) := div_le_div_of_nonneg_right hg (Real.rpow_nonneg (by positivity) (p + 1)) _ = Cg * (((u + 1 : ℕ) : ℝ) ^ q / ((u + 1 : ℕ) : ℝ) ^ (p + 1)) := by ring _ = Cg * ((u + 1 : ℕ) : ℝ) ^ (q - p - 1) := by rw [show q - p - 1 = q - (p + 1) by ring] rw [Real.rpow_sub (by positivity : 0 < ((u + 1 : ℕ) : ℝ)) q (p + 1)] _ = Cg * ∑ u ∈ Finset.range n, ((u + 1 : ℕ) : ℝ) ^ (q - p - 1) := by rw [Finset.mul_sum] _ ≤ Cg * ∑ v ∈ Finset.range (n + 1), (v : ℝ) ^ (q - p - 1) := by apply mul_le_mul_of_nonneg_left _ hCpos.le rw [Finset.sum_range_succ' (fun v => (v : ℝ) ^ (q - p - 1)) n] exact le_add_of_nonneg_right (by positivity) _ ≤ Cg * (∑' v : ℕ, (v : ℝ) ^ (q - p - 1)) := by exact mul_le_mul_of_nonneg_left (Summable.sum_le_tsum (Finset.range (n + 1)) (by intro v _hv; exact Real.rpow_nonneg (by positivity) (q - p - 1)) hsum) hCpos.le

The upper comparison

The integral tail I n - I m is at least the number of terms times the smallest term.

lemma akraBazziIntegral_tail_lower {p : ℝ} {g : ℕ → ℝ} (hp : 0 ≤ p) (hgnonneg : ∀ n, 0 ≤ g n) (hgmono : ∀ {m n : ℕ}, m ≤ n → g m ≤ g n) {m n : ℕ} (hmn : m ≤ n) : ((n - m : ℕ) : ℝ) * (g m / (n : ℝ) ^ (p + 1)) ≤ akraBazziIntegral p g n - akraBazziIntegral p g m := by rw [akraBazziIntegral_sub hmn] rw [show ((n - m : ℕ) : ℝ) * (g m / (n : ℝ) ^ (p + 1)) = ∑ u ∈ Finset.range (n - m), (g m / (n : ℝ) ^ (p + 1)) by rw [Finset.sum_const, nsmul_eq_mul, Finset.card_range]] apply Finset.sum_le_sum intro u hu have hle_n : m + u + 1 ≤ n := by have hu_lt : u < n - m := by simpa [Finset.mem_range] using hu omega have hge_m : m ≤ m + u + 1 := by omega have hg : g m ≤ g (m + u + 1) := hgmono hge_m have hpow : ((m + u + 1 : ℕ) : ℝ) ^ (p + 1) ≤ (n : ℝ) ^ (p + 1) := Real.rpow_le_rpow (by positivity) (by exact_mod_cast hle_n) (by linarith : 0 ≤ p + 1) have h1 : g m / (n : ℝ) ^ (p + 1) ≤ g (m + u + 1) / (n : ℝ) ^ (p + 1) := div_le_div_of_nonneg_right hg (Real.rpow_nonneg (by positivity) (p + 1)) exact le_trans h1 (div_le_div_of_nonneg_left (hgnonneg (m + u + 1)) (Real.rpow_pos_of_pos (by positivity) (p + 1)) hpow)

The single-branch integral increment a (n/b)^p (I n - I ⌊n/b⌋) dominates g n by a positive factor eventually. This is the analytic core of the upper-bound substitution proof.

lemma akraBazzi_increment_lower {a : ℕ} {b p q : ℝ} {g : ℕ → ℝ} (ha : 0 < a) (hb : 1 < b) (hp : 0 ≤ p) (hq : 0 ≤ q) (hsmooth : PolynomialGrowth g q) : ∃ ε : ℝ, 0 < ε ∧ ∃ n₁ : ℕ, ∀ n, n₁ ≤ n → (a : ℝ) * ((n : ℝ) / b) ^ p * (akraBazziIntegral p g n - akraBazziIntegral p g (⌊(n : ℝ) / b⌋₊)) ≥ ε * g n := by rcases hsmooth with ⟨hgnonneg, hgmono, c, C, hcpos, hCpos, hglower, hgupper⟩ have hb_pos : 0 < b := lt_trans (by norm_num : (0 : ℝ) < 1) hb have ha_pos : 0 < (a : ℝ) := by exact_mod_cast ha let ε : ℝ := (a : ℝ) * (1 - 1 / b) * b ^ (-p) * c * (2 * b) ^ (-q) / C have hsub_pos : 0 < 1 - 1 / b := by have h1 : (1 : ℝ) / b < 1 := (div_lt_one hb_pos).mpr hb linarith have hε_pos : 0 < ε := by dsimp [ε] positivity let n₁ : ℕ := Nat.ceil (2 * b) + 1 refine ⟨ε, hε_pos, n₁, ?_⟩ intro n hn have hn_2b : 2 * b ≤ (n : ℝ) := by have hceil : 2 * b ≤ (Nat.ceil (2 * b) : ℝ) := Nat.le_ceil (2 * b) have hn₁' : (Nat.ceil (2 * b) + 1 : ℝ) ≤ (n : ℝ) := by exact_mod_cast hn have hceil1 : (Nat.ceil (2 * b) : ℝ) ≤ (Nat.ceil (2 * b) + 1 : ℝ) := by norm_num linarith have hn_pos_nat : 0 < n := by have h2b : 0 < 2 * b := by positivity exact_mod_cast (lt_of_lt_of_le h2b hn_2b) have hn_pos : 0 < (n : ℝ) := by exact_mod_cast hn_pos_nat let m : ℕ := ⌊(n : ℝ) / b⌋₊ have hm_lt_n : m < n := floor_div_lt_self hb hn_pos_nat have hm_le : (m : ℝ) ≤ (n : ℝ) / b := Nat.floor_le (le_of_lt (div_pos hn_pos hb_pos)) have hm_ge_half : (n : ℝ) / (2 * b) ≤ (m : ℝ) := by have hfl : (n : ℝ) / b - 1 ≤ (m : ℝ) := by have hlt : (n : ℝ) / b < (m : ℝ) + 1 := Nat.lt_floor_add_one ((n : ℝ) / b) linarith have h2 : (n : ℝ) / (2 * b) ≤ (n : ℝ) / b - 1 := by field_simp [hb_pos] linarith exact le_trans h2 hfl have hm_ge_one : 1 ≤ m := by have hm1 : (1 : ℝ) ≤ (m : ℝ) := by have h : (1 : ℝ) ≤ (n : ℝ) / (2 * b) := by rw [le_div_iff₀ (by positivity : 0 < (2 * b : ℝ))] simpa using hn_2b exact le_trans h hm_ge_half exact_mod_cast hm1 have hg_m : c * (2 * b) ^ (-q) / C * g n ≤ g m := by have hm_q : c * (m : ℝ) ^ q ≤ g m := hglower m hm_ge_one have hgn : g n ≤ C * (n : ℝ) ^ q := hgupper n hn_pos_nat have hm_nq : c * (2 * b) ^ (-q) * (n : ℝ) ^ q ≤ g m := by have hmn : (n : ℝ) / (2 * b) ≤ (m : ℝ) := hm_ge_half have hpow : ((n : ℝ) / (2 * b)) ^ q ≤ (m : ℝ) ^ q := Real.rpow_le_rpow (by positivity) hmn hq have h1 : c * ((n : ℝ) / (2 * b)) ^ q ≤ g m := le_trans (mul_le_mul_of_nonneg_left hpow hcpos.le) hm_q have h2 : ((n : ℝ) / (2 * b)) ^ q = (n : ℝ) ^ q * (2 * b) ^ (-q) := by rw [Real.div_rpow (by positivity) (by positivity : 0 ≤ 2 * b)] rw [Real.rpow_neg (by positivity : 0 ≤ 2 * b) q] rw [div_eq_mul_inv] rw [h2] at h1 simpa [mul_comm, mul_left_comm, mul_assoc] using h1 have h3 : c * (2 * b) ^ (-q) / C * g n ≤ c * (2 * b) ^ (-q) * (n : ℝ) ^ q := by have hgn_div : g n / C ≤ (n : ℝ) ^ q := by rw [div_le_iff₀ hCpos] simpa [mul_comm] using hgn have hnn : c * (2 * b) ^ (-q) * (g n / C) ≤ c * (2 * b) ^ (-q) * (n : ℝ) ^ q := mul_le_mul_of_nonneg_left hgn_div (mul_nonneg hcpos.le (Real.rpow_nonneg (by positivity) (-q))) have hrewrite : c * (2 * b) ^ (-q) / C * g n = c * (2 * b) ^ (-q) * (g n / C) := by field_simp [ne_of_gt hCpos] rwa [hrewrite] exact le_trans h3 hm_nq have htail : ((n - m : ℕ) : ℝ) * (g m / (n : ℝ) ^ (p + 1)) ≤ akraBazziIntegral p g n - akraBazziIntegral p g m := akraBazziIntegral_tail_lower hp hgnonneg hgmono (le_of_lt hm_lt_n) have hn_card : (n : ℝ) * (1 - 1 / b) ≤ (n - m : ℕ) := by have hsub : (n : ℝ) - (n : ℝ) / b ≤ (n - m : ℕ) := by rw [Nat.cast_sub (le_of_lt hm_lt_n)] linarith have h1 : (n : ℝ) * (1 - 1 / b) = (n : ℝ) - (n : ℝ) / b := by ring rw [h1] exact hsub have hid : ((n : ℝ) / b) ^ p * (n : ℝ) / (n : ℝ) ^ (p + 1) = b ^ (-p) := by rw [Real.div_rpow (by positivity) hb_pos.le] rw [Real.rpow_add hn_pos p 1, Real.rpow_one] field_simp [ne_of_gt (Real.rpow_pos_of_pos hn_pos p), ne_of_gt (Real.rpow_pos_of_pos hb_pos p)] rw [Real.rpow_neg hb_pos.le p] exact (mul_inv_cancel₀ (ne_of_gt (Real.rpow_pos_of_pos hb_pos p))).symm have halg : (a : ℝ) * ((n : ℝ) / b) ^ p * (((n : ℝ) * (1 - 1 / b)) * (g m / (n : ℝ) ^ (p + 1))) = (a : ℝ) * (1 - 1 / b) * b ^ (-p) * (g m) := by rw [div_eq_mul_inv] calc (a : ℝ) * ((n : ℝ) / b) ^ p * (((n : ℝ) * (1 - 1 / b)) * (g m * ((n : ℝ) ^ (p + 1))⁻¹)) = (a : ℝ) * (1 - 1 / b) * (g m) * (((n : ℝ) / b) ^ p * (n : ℝ) * ((n : ℝ) ^ (p + 1))⁻¹) := by ring _ = (a : ℝ) * (1 - 1 / b) * (g m) * b ^ (-p) := by rw [show ((n : ℝ) / b) ^ p * (n : ℝ) * ((n : ℝ) ^ (p + 1))⁻¹ = b ^ (-p) by simpa [div_eq_mul_inv] using hid] _ = (a : ℝ) * (1 - 1 / b) * b ^ (-p) * (g m) := by ring calc (a : ℝ) * ((n : ℝ) / b) ^ p * (akraBazziIntegral p g n - akraBazziIntegral p g m) ≥ (a : ℝ) * ((n : ℝ) / b) ^ p * (((n - m : ℕ) : ℝ) * (g m / (n : ℝ) ^ (p + 1))) := mul_le_mul_of_nonneg_left htail (mul_nonneg ha_pos.le (Real.rpow_nonneg (by positivity) p)) _ ≥ (a : ℝ) * ((n : ℝ) / b) ^ p * (((n : ℝ) * (1 - 1 / b)) * (g m / (n : ℝ) ^ (p + 1))) := mul_le_mul_of_nonneg_left (mul_le_mul_of_nonneg_right (by exact_mod_cast hn_card) (div_nonneg (hgnonneg m) (Real.rpow_nonneg (by positivity) (p + 1)))) (mul_nonneg ha_pos.le (Real.rpow_nonneg (by positivity) p)) _ = (a : ℝ) * (1 - 1 / b) * b ^ (-p) * (g m) := halg _ ≥ (a : ℝ) * (1 - 1 / b) * b ^ (-p) * (c * (2 * b) ^ (-q) / C * g n) := mul_le_mul_of_nonneg_left hg_m (mul_nonneg (mul_nonneg ha_pos.le (le_of_lt hsub_pos)) (Real.rpow_nonneg hb_pos.le (-p))) _ = ε * g n := by dsimp [ε]; ring

The increment upper bound (smallness)

The integral tail I n - I m is at most the number of terms times the largest term g n / m^(p+1). This is the companion of akraBazziIntegral_tail_lower: the tail is squeezed between the smallest and largest term counts.

lemma akraBazziIntegral_tail_upper {p : ℝ} {g : ℕ → ℝ} (hp : 0 ≤ p) (hgnonneg : ∀ n, 0 ≤ g n) (hgmono : ∀ {m n : ℕ}, m ≤ n → g m ≤ g n) {m n : ℕ} (hmn : m ≤ n) (hm_pos : 0 < m) : akraBazziIntegral p g n - akraBazziIntegral p g m ≤ ((n - m : ℕ) : ℝ) * g n / (m : ℝ) ^ (p + 1) := by rw [akraBazziIntegral_sub hmn] have hm_pos' : 0 < (m : ℝ) := by exact_mod_cast hm_pos have hle_each : ∀ u ∈ Finset.range (n - m), g (m + u + 1) / ((m + u + 1 : ℕ) : ℝ) ^ (p + 1) ≤ g n / (m : ℝ) ^ (p + 1) := by intro u hu have hu_lt : u < n - m := by simpa [Finset.mem_range] using hu have hle_n : m + u + 1 ≤ n := by omega have hge_m : m ≤ m + u + 1 := by omega have hg : g (m + u + 1) ≤ g n := hgmono hle_n have hpow : (m : ℝ) ^ (p + 1) ≤ ((m + u + 1 : ℕ) : ℝ) ^ (p + 1) := Real.rpow_le_rpow (by positivity) (by exact_mod_cast hge_m) (by linarith : 0 ≤ p + 1) have hdiv : g (m + u + 1) / ((m + u + 1 : ℕ) : ℝ) ^ (p + 1) ≤ g n / ((m + u + 1 : ℕ) : ℝ) ^ (p + 1) := div_le_div_of_nonneg_right hg (Real.rpow_nonneg (by positivity) (p + 1)) exact le_trans hdiv (div_le_div_of_nonneg_left (hgnonneg n) (Real.rpow_pos_of_pos (by positivity) (p + 1)) hpow) calc ∑ u ∈ Finset.range (n - m), g (m + u + 1) / ((m + u + 1 : ℕ) : ℝ) ^ (p + 1) ≤ ∑ u ∈ Finset.range (n - m), g n / (m : ℝ) ^ (p + 1) := by exact Finset.sum_le_sum (fun u hu => hle_each u hu) _ = ((n - m : ℕ) : ℝ) * (g n / (m : ℝ) ^ (p + 1)) := by rw [Finset.sum_const, nsmul_eq_mul, Finset.card_range] _ = ((n - m : ℕ) : ℝ) * g n / (m : ℝ) ^ (p + 1) := by ring

A helper: foldr max 0 l is at least every element of l.

lemma List_foldr_max_ge_mem {l : List ℕ} {x : ℕ} (hx : x ∈ l) : x ≤ l.foldr max 0 := by induction l with | nil => simp at hx | cons h t ih => rw [List.foldr_cons] simp only [List.mem_cons] at hx rcases hx with rfl | hx' · exact le_max_left _ _ · exact le_trans (ih hx') (le_max_right _ _)

The single-branch integral increment a (n/b)^p (I n - I ⌊n/b⌋) is at most a constant multiple of g n. This is the analytic core of the lower-bound substitution proof: the increment lost to the floor rounding is comparable to (not larger than) the forcing term g n.

lemma akraBazzi_increment_upper_single {a : ℕ} {b p : ℝ} {g : ℕ → ℝ} (Variable name `ha` is not explicitly referenced. The binding can be removed (if unused) or named `_` (if used implicitly). Note: This linter can be disabled with `set_option linter.unusedVariables false`ha : 0 < a) (hb : 1 < b) (hp : 0 ≤ p) (hgnonneg : ∀ n, 0 ≤ g n) (hgmono : ∀ {m n : ℕ}, m ≤ n → g m ≤ g n) : ∀ n : ℕ, Nat.ceil (2 * b) + 1 ≤ n → akraBazziIncrement p g (a, b) n ≤ (a : ℝ) * 2 ^ (p + 1) * b * g n := by have hb_pos : 0 < b := lt_trans (by norm_num : (0 : ℝ) < 1) hb intro n hn have hn_2b : 2 * b ≤ (n : ℝ) := by have hceil : 2 * b ≤ (Nat.ceil (2 * b) : ℝ) := Nat.le_ceil (2 * b) have hn₁' : (Nat.ceil (2 * b) + 1 : ℝ) ≤ (n : ℝ) := by exact_mod_cast hn have hceil1 : (Nat.ceil (2 * b) : ℝ) ≤ (Nat.ceil (2 * b) + 1 : ℝ) := by norm_num linarith have hn_pos_nat : 0 < n := by have h2b : 0 < 2 * b := by positivity exact_mod_cast (lt_of_lt_of_le h2b hn_2b) have hn_pos : 0 < (n : ℝ) := by exact_mod_cast hn_pos_nat let m : ℕ := ⌊(n : ℝ) / b⌋₊ have hm_lt_n : m < n := floor_div_lt_self hb hn_pos_nat have hm_le_n : m ≤ n := le_of_lt hm_lt_n have hm_ge_half : (n : ℝ) / (2 * b) ≤ (m : ℝ) := by have hfl : (n : ℝ) / b - 1 ≤ (m : ℝ) := by have hlt : (n : ℝ) / b < (m : ℝ) + 1 := Nat.lt_floor_add_one ((n : ℝ) / b) linarith have h2 : (n : ℝ) / (2 * b) ≤ (n : ℝ) / b - 1 := by field_simp [hb_pos] linarith exact le_trans h2 hfl have hm_ge_one_nat : 1 ≤ m := by have hm1 : (1 : ℝ) ≤ (m : ℝ) := by have h : (1 : ℝ) ≤ (n : ℝ) / (2 * b) := by rw [le_div_iff₀ (by positivity : 0 < (2 * b : ℝ))] simpa using hn_2b exact le_trans h hm_ge_half exact_mod_cast hm1 have hm_pos : 0 < m := lt_of_lt_of_le (by norm_num : (0 : ℕ) < 1) hm_ge_one_nat have hm_pos' : 0 < (m : ℝ) := by exact_mod_cast hm_pos have htail : akraBazziIntegral p g n - akraBazziIntegral p g m ≤ ((n - m : ℕ) : ℝ) * g n / (m : ℝ) ^ (p + 1) := akraBazziIntegral_tail_upper hp hgnonneg hgmono hm_le_n hm_pos have hnm : ((n - m : ℕ) : ℝ) ≤ (n : ℝ) := by have : (n - m : ℕ) ≤ n := Nat.sub_le n m exact_mod_cast this have hm_pow_le : ((n : ℝ) / (2 * b)) ^ (p + 1) ≤ (m : ℝ) ^ (p + 1) := Real.rpow_le_rpow (by positivity) hm_ge_half (by linarith : 0 ≤ p + 1) have htail2 : ((n - m : ℕ) : ℝ) * g n / (m : ℝ) ^ (p + 1) ≤ (2 * b) ^ (p + 1) * g n / (n : ℝ) ^ p := by have hnum : ((n - m : ℕ) : ℝ) * g n ≤ (n : ℝ) * g n := mul_le_mul_of_nonneg_right hnm (hgnonneg n) have hdiv_le : (n : ℝ) * g n / (m : ℝ) ^ (p + 1) ≤ (n : ℝ) * g n / ((n : ℝ) / (2 * b)) ^ (p + 1) := div_le_div_of_nonneg_left (mul_nonneg hn_pos.le (hgnonneg n)) (Real.rpow_pos_of_pos (div_pos hn_pos (by positivity : 0 < 2 * b)) (p + 1)) hm_pow_le have hid : (n : ℝ) * g n / ((n : ℝ) / (2 * b)) ^ (p + 1) = (2 * b) ^ (p + 1) * g n / (n : ℝ) ^ p := by rw [Real.div_rpow (by positivity) (by positivity : 0 ≤ 2 * b)] rw [div_div_eq_mul_div] have h2 : (n : ℝ) / (n : ℝ) ^ (p + 1) = 1 / (n : ℝ) ^ p := by rw [show (n : ℝ) ^ (p + 1) = (n : ℝ) ^ p * (n : ℝ) by rw [Real.rpow_add hn_pos p 1, Real.rpow_one]] field_simp [ne_of_gt hn_pos, ne_of_gt (Real.rpow_pos_of_pos hn_pos p)] calc (n : ℝ) * g n * (2 * b) ^ (p + 1) / (n : ℝ) ^ (p + 1) = ((n : ℝ) / (n : ℝ) ^ (p + 1)) * g n * (2 * b) ^ (p + 1) := by ring _ = (1 / (n : ℝ) ^ p) * g n * (2 * b) ^ (p + 1) := by rw [h2] _ = (2 * b) ^ (p + 1) * g n / (n : ℝ) ^ p := by ring calc ((n - m : ℕ) : ℝ) * g n / (m : ℝ) ^ (p + 1) ≤ (n : ℝ) * g n / (m : ℝ) ^ (p + 1) := div_le_div_of_nonneg_right hnum (Real.rpow_nonneg (by positivity) (p + 1)) _ ≤ (n : ℝ) * g n / ((n : ℝ) / (2 * b)) ^ (p + 1) := hdiv_le _ = (2 * b) ^ (p + 1) * g n / (n : ℝ) ^ p := hid have halg : (a : ℝ) * ((n : ℝ) / b) ^ p * ((2 * b) ^ (p + 1) * g n / (n : ℝ) ^ p) = (a : ℝ) * 2 ^ (p + 1) * b * g n := by have hquot : ((n : ℝ) / b) ^ p / (n : ℝ) ^ p = b ^ (-p) := by rw [Real.div_rpow (by positivity) hb_pos.le] rw [Real.rpow_neg hb_pos.le p] rw [show (n : ℝ) ^ p / b ^ p / (n : ℝ) ^ p = (b ^ p)⁻¹ by rw [show (n : ℝ) ^ p / b ^ p / (n : ℝ) ^ p = (n : ℝ) ^ p * (b ^ p)⁻¹ * ((n : ℝ) ^ p)⁻¹ by rw [div_eq_mul_inv] rfl] calc (n : ℝ) ^ p * (b ^ p)⁻¹ * ((n : ℝ) ^ p)⁻¹ = (b ^ p)⁻¹ * ((n : ℝ) ^ p * ((n : ℝ) ^ p)⁻¹) := by ring _ = (b ^ p)⁻¹ * 1 := by rw [mul_inv_cancel₀ (ne_of_gt (Real.rpow_pos_of_pos hn_pos p))] _ = (b ^ p)⁻¹ := by ring] have hmul : b ^ (-p) * (2 * b) ^ (p + 1) = 2 ^ (p + 1) * b := by rw [Real.mul_rpow (by norm_num : 0 ≤ (2 : ℝ)) hb_pos.le] have h1 : b ^ (-p) * b ^ (p + 1) = b := by rw [← Real.rpow_add hb_pos (-p) (p + 1)] rw [show -p + (p + 1) = 1 by ring] rw [Real.rpow_one] calc b ^ (-p) * (2 ^ (p + 1) * b ^ (p + 1)) = 2 ^ (p + 1) * (b ^ (-p) * b ^ (p + 1)) := by ring _ = 2 ^ (p + 1) * b := by rw [h1] calc (a : ℝ) * ((n : ℝ) / b) ^ p * ((2 * b) ^ (p + 1) * g n / (n : ℝ) ^ p) = (a : ℝ) * (((n : ℝ) / b) ^ p / (n : ℝ) ^ p) * (2 * b) ^ (p + 1) * g n := by ring _ = (a : ℝ) * b ^ (-p) * (2 * b) ^ (p + 1) * g n := by rw [hquot] _ = (a : ℝ) * (2 ^ (p + 1) * b) * g n := by rw [show (a : ℝ) * b ^ (-p) * (2 * b) ^ (p + 1) * g n = (a : ℝ) * (b ^ (-p) * (2 * b) ^ (p + 1)) * g n by ring] rw [hmul] _ = (a : ℝ) * 2 ^ (p + 1) * b * g n := by ring calc akraBazziIncrement p g (a, b) n = (a : ℝ) * ((n : ℝ) / b) ^ p * (akraBazziIntegral p g n - akraBazziIntegral p g m) := by rfl _ ≤ (a : ℝ) * ((n : ℝ) / b) ^ p * (((n - m : ℕ) : ℝ) * g n / (m : ℝ) ^ (p + 1)) := mul_le_mul_of_nonneg_left htail (mul_nonneg (Nat.cast_nonneg a) (Real.rpow_nonneg (by positivity) p)) _ ≤ (a : ℝ) * ((n : ℝ) / b) ^ p * ((2 * b) ^ (p + 1) * g n / (n : ℝ) ^ p) := mul_le_mul_of_nonneg_left htail2 (mul_nonneg (Nat.cast_nonneg a) (Real.rpow_nonneg (by positivity) p)) _ = (a : ℝ) * 2 ^ (p + 1) * b * g n := halg

The multi-branch increment Σᵢ aᵢ (n/bᵢ)^p (I n - I ⌊n/bᵢ⌋) is at most a constant multiple of g n. Each branch is bounded by akraBazzi_increment_upper_single, and the terms are summed.

lemma akraBazzi_increment_upper {branches : List (ℕ × ℝ)} {p : ℝ} {g : ℕ → ℝ} (hvalid : BranchesValid branches) (hnonempty : branches ≠ []) (hp : 0 ≤ p) (hgnonneg : ∀ n, 0 ≤ g n) (hgmono : ∀ {m n : ℕ}, m ≤ n → g m ≤ g n) : ∃ K : ℝ, 0 < K ∧ ∃ n₁ : ℕ, ∀ n, n₁ ≤ n → (branches.map (fun ab => akraBazziIncrement p g ab n)).sum ≤ K * g n := by let K : ℝ := (2 : ℝ) ^ (p + 1) * (branches.map (fun ab => (ab.1 : ℝ) * ab.2)).sum have hK_pos : 0 < K := by dsimp [K] have h2 : 0 < (2 : ℝ) ^ (p + 1) := Real.rpow_pos_of_pos (by norm_num : 0 < (2 : ℝ)) (p + 1) have hsum : 0 < (branches.map (fun ab => (ab.1 : ℝ) * ab.2)).sum := by rcases branches with _ | ⟨ab, rest⟩ · contradiction · have hvalid_ab : BranchValid ab := hvalid ab (by simp) have hab : 0 < (ab.1 : ℝ) * ab.2 := by have ha : 0 < (ab.1 : ℝ) := by exact_mod_cast hvalid_ab.1 have hb : 0 < ab.2 := lt_trans (by norm_num : (0 : ℝ) < 1) hvalid_ab.2 exact mul_pos ha hb have hrest_nonneg : 0 ≤ (rest.map (fun x => (x.1 : ℝ) * x.2)).sum := by apply List.sum_nonneg intro y hy rw [List.mem_map] at hy rcases hy with ⟨x, _hx, rfl⟩ have hvalid_x : BranchValid x := hvalid x (by simp [_hx]) exact mul_nonneg (Nat.cast_nonneg x.1) (le_of_lt (lt_trans (by norm_num : (0 : ℝ) < 1) hvalid_x.2)) exact lt_of_lt_of_le hab (by rw [List.map_cons, List.sum_cons] exact le_add_of_nonneg_right hrest_nonneg) exact mul_pos h2 hsum let n₁ : ℕ := (branches.map (fun ab => Nat.ceil (2 * ab.2) + 1)).foldr max 0 refine ⟨K, hK_pos, n₁, ?_⟩ intro n hn have hsum_le : (branches.map (fun ab => akraBazziIncrement p g ab n)).sum ≤ (branches.map (fun ab => (ab.1 : ℝ) * 2 ^ (p + 1) * ab.2 * g n)).sum := by apply List.sum_le_sum intro ab hab have hvalid_ab : BranchValid ab := hvalid ab hab have hx_threshold : Nat.ceil (2 * ab.2) + 1 ≤ n := by have hx_in_fold : Nat.ceil (2 * ab.2) + 1 ≤ n₁ := by dsimp [n₁] exact List_foldr_max_ge_mem (by rw [List.mem_map] refine ⟨ab, hab, rfl⟩) exact le_trans hx_in_fold hn exact akraBazzi_increment_upper_single (a := ab.1) (b := ab.2) (p := p) (g := g) hvalid_ab.1 hvalid_ab.2 hp hgnonneg hgmono n hx_threshold calc (branches.map (fun ab => akraBazziIncrement p g ab n)).sum ≤ (branches.map (fun ab => (ab.1 : ℝ) * 2 ^ (p + 1) * ab.2 * g n)).sum := hsum_le _ = ((2 : ℝ) ^ (p + 1) * (branches.map (fun ab => (ab.1 : ℝ) * ab.2)).sum) * g n := by rw [show (branches.map (fun ab => (ab.1 : ℝ) * 2 ^ (p + 1) * ab.2 * g n)).sum = ((2 : ℝ) ^ (p + 1) * (branches.map (fun ab => (ab.1 : ℝ) * ab.2)).sum) * g n by calc (branches.map (fun ab => (ab.1 : ℝ) * 2 ^ (p + 1) * ab.2 * g n)).sum = (branches.map (fun ab => (2 : ℝ) ^ (p + 1) * g n * ((ab.1 : ℝ) * ab.2))).sum := by exact congrArg List.sum (List.map_congr_left (by intro ab _hab; ring)) _ = (2 : ℝ) ^ (p + 1) * g n * (branches.map (fun ab => (ab.1 : ℝ) * ab.2)).sum := by rw [List.sum_map_mul_left branches (fun ab => (ab.1 : ℝ) * ab.2) ((2 : ℝ) ^ (p + 1) * g n)] _ = ((2 : ℝ) ^ (p + 1) * (branches.map (fun ab => (ab.1 : ℝ) * ab.2)).sum) * g n := by ring] _ = K * g n := by rfl

The multi-branch increment Σᵢ aᵢ (n/bᵢ)^p (I n - I ⌊n/bᵢ⌋) is at least a positive constant multiple of g n. One branch is bounded below by akraBazzi_increment_lower; the other branches contribute nonnegatively.

lemma akraBazzi_increment_lower_multi {branches : List (ℕ × ℝ)} {p q : ℝ} {g : ℕ → ℝ} (hvalid : BranchesValid branches) (hnonempty : branches ≠ []) (hp : 0 ≤ p) (hq : 0 ≤ q) (hsmooth : PolynomialGrowth g q) : ∃ ε : ℝ, 0 < ε ∧ ∃ n₁ : ℕ, ∀ n, n₁ ≤ n → ε * g n ≤ (branches.map (fun ab => akraBazziIncrement p g ab n)).sum := by have hgnonneg : ∀ n, 0 ≤ g n := hsmooth.1 rcases branches with _ | ⟨ab, rest⟩ · contradiction · have hvalid_ab : BranchValid ab := hvalid ab (by simp) rcases akraBazzi_increment_lower (a := ab.1) (b := ab.2) (p := p) (q := q) (g := g) hvalid_ab.1 hvalid_ab.2 hp hq hsmooth with ⟨ε, hε, n₁, hn₁⟩ refine ⟨ε, hε, n₁, ?_⟩ intro n hn have hrest_nonneg : 0 ≤ (rest.map (fun x : ℕ × ℝ => akraBazziIncrement p g x n)).sum := by apply List.sum_nonneg intro x hx rw [List.mem_map] at hx rcases hx with ⟨y, _hy, rfl⟩ have hvalid_y : BranchValid y := hvalid y (by simp [_hy]) have hdiff_nonneg : 0 ≤ akraBazziIntegral p g n - akraBazziIntegral p g (⌊(n : ℝ) / y.2⌋₊) := by have hle : ⌊(n : ℝ) / y.2⌋₊ ≤ n := by have hy2 : 1 < y.2 := hvalid_y.2 have hpos2 : 0 < y.2 := lt_trans (by norm_num : (0 : ℝ) < 1) hy2 have hfl : (⌊(n : ℝ) / y.2⌋₊ : ℝ) ≤ (n : ℝ) / y.2 := Nat.floor_le (div_nonneg (Nat.cast_nonneg n) (le_of_lt hpos2)) have hdiv_le : (n : ℝ) / y.2 ≤ (n : ℝ) := by rw [div_le_iff₀ hpos2] simpa [one_mul, mul_comm] using mul_le_mul_of_nonneg_right (le_of_lt hy2) (Nat.cast_nonneg n) exact_mod_cast (le_trans hfl hdiv_le) exact sub_nonneg.mpr (akraBazziIntegral_mono hgnonneg hle) exact mul_nonneg (mul_nonneg (Nat.cast_nonneg y.1) (Real.rpow_nonneg (div_nonneg (Nat.cast_nonneg n) (le_of_lt (lt_trans (by norm_num : (0 : ℝ) < 1) hvalid_y.2))) p)) hdiff_nonneg calc ε * g n ≤ akraBazziIncrement p g ab n := hn₁ n hn _ ≤ akraBazziIncrement p g ab n + (rest.map (fun x : ℕ × ℝ => akraBazziIncrement p g x n)).sum := le_add_of_nonneg_right hrest_nonneg _ = ((ab :: rest).map (fun x : ℕ × ℝ => akraBazziIncrement p g x n)).sum := by rfl

The recurrence-to-integral comparison

The Akra–Bazzi scale is at least 1 whenever n ≥ 1.

lemma akraBazzi_scale_ge_one {p : ℝ} {g : ℕ → ℝ} (hp : 0 ≤ p) (hgnonneg : ∀ n, 0 ≤ g n) {n : ℕ} (hn : 1 ≤ n) : 1 ≤ akraBazziScale p g n := by unfold akraBazziScale have hn' : 1 ≤ (n : ℝ) := by exact_mod_cast hn have hnp : 1 ≤ (n : ℝ) ^ p := by calc 1 = (1 : ℝ) ^ p := by rw [Real.one_rpow] _ ≤ (n : ℝ) ^ p := Real.rpow_le_rpow (by norm_num : 0 ≤ (1 : ℝ)) hn' hp have hI : 0 ≤ akraBazziIntegral p g n := akraBazziIntegral_nonneg hgnonneg n have h1I : 1 ≤ 1 + akraBazziIntegral p g n := by linarith exact one_le_mul_of_one_le_of_one_le hnp h1I

The decomposition of the scale at one level of the recursion tree: the weighted sum of the children's scales Σᵢ aᵢ (n/bᵢ)^p (1 + I ⌊n/bᵢ⌋) equals the scale n^p (1 + I n) minus the total increment Σᵢ aᵢ (n/bᵢ)^p (I n - I ⌊n/bᵢ⌋).

lemma akraBazzi_scale_decomp (branches : List (ℕ × ℝ)) (p : ℝ) (g : ℕ → ℝ) (n : ℕ) (hvalid : BranchesValid branches) (hroot : IsAkraBazziRoot branches p) : (branches.map (fun ab => (ab.1 : ℝ) * ((n : ℝ) / ab.2) ^ p * (1 + akraBazziIntegral p g (⌊(n : ℝ) / ab.2⌋₊)))).sum = akraBazziScale p g n - (branches.map (fun ab => akraBazziIncrement p g ab n)).sum := by unfold akraBazziScale akraBazziIncrement have hsum : (branches.map (fun ab => (ab.1 : ℝ) * ((n : ℝ) / ab.2) ^ p * (1 + akraBazziIntegral p g (⌊(n : ℝ) / ab.2⌋₊)))).sum + (branches.map (fun ab => (ab.1 : ℝ) * ((n : ℝ) / ab.2) ^ p * (akraBazziIntegral p g n - akraBazziIntegral p g (⌊(n : ℝ) / ab.2⌋₊)))).sum = (n : ℝ) ^ p * (1 + akraBazziIntegral p g n) := by rw [← List.sum_map_add] have hmap : (branches.map (fun ab => (ab.1 : ℝ) * ((n : ℝ) / ab.2) ^ p * (1 + akraBazziIntegral p g (⌊(n : ℝ) / ab.2⌋₊)) + (ab.1 : ℝ) * ((n : ℝ) / ab.2) ^ p * (akraBazziIntegral p g n - akraBazziIntegral p g (⌊(n : ℝ) / ab.2⌋₊)))).sum = (branches.map (fun ab => (ab.1 : ℝ) * ((n : ℝ) / ab.2) ^ p * (1 + akraBazziIntegral p g n))).sum := by exact congrArg List.sum (List.map_congr_left (by intro ab _hab; ring)) rw [hmap] rw [List.sum_map_mul_right branches (fun ab => (ab.1 : ℝ) * ((n : ℝ) / ab.2) ^ p) (1 + akraBazziIntegral p g n)] rw [akraBazzi_root_scale_invariance branches p hvalid hroot n] linarith

A solution of an Akra–Bazzi recurrence is nonnegative.

try 'simp' instead of 'simpa' Note: This linter can be disabled with `set_option linter.unnecessarySimpa false` lemma akraBazzi_T_nonneg {branches : List (ℕ × ℝ)} {g T : ℕ → ℝ} {n₀ : ℕ} (hvalid : BranchesValid branches) (hgnonneg : ∀ n, 0 ≤ g n) (hsat : SatisfiesAkraBazzi branches g T n₀) : ∀ n, 0 ≤ T n := by intro n induction n using Nat.strong_induction_on with | h n ih => by_cases hn0 : n = 0 · subst n try 'simp' instead of 'simpa' Note: This linter can be disabled with `set_option linter.unnecessarySimpa false`simpa [hsat.1] · have hn_pos : 0 < n := Nat.pos_of_ne_zero hn0 by_cases hle : n ≤ n₀ · rw [hsat.2.1 n (Nat.succ_le_iff.mpr hn_pos) hle] norm_num · have hn₀n : n₀ < n := lt_of_not_ge hle rw [hsat.2.2 n hn₀n] exact add_nonneg (by apply List.sum_nonneg intro x hx rw [List.mem_map] at hx rcases hx with ⟨ab, hab, rfl⟩ have hvalid_ab : BranchValid ab := hvalid ab hab have hlt : ⌊(n : ℝ) / ab.2⌋₊ < n := floor_div_lt_self hvalid_ab.2 hn_pos exact mul_nonneg (Nat.cast_nonneg ab.1) (ih (⌊(n : ℝ) / ab.2⌋₊) hlt)) (hgnonneg n)

Akra–Bazzi upper bound. The solution T of the multi-branch recurrence is O(n^p (1 + I n)) where I n = Σ_{u≤n} g(u)/u^(p+1). This is the upper half of the recurrence-to-integral comparison: the forcing g is absorbed by the per-level increment akraBazziIncrement, which is bounded below by akraBazzi_increment_lower_multi.

theorem akraBazzi_upper_bound_nonneg {branches : List (ℕ × ℝ)} {g T : ℕ → ℝ} {n₀ : ℕ} {p q : ℝ} (hvalid : BranchesValid branches) (hnonempty : branches ≠ []) (hroot : IsAkraBazziRoot branches p) (hp : 0 ≤ p) (hq : 0 ≤ q) (hsmooth : PolynomialGrowth g q) (hsat : SatisfiesAkraBazzi branches g T n₀) : Chapter03.isBigO T (akraBazziScale p g) := by have hgnonneg : ∀ n, 0 ≤ g n := hsmooth.1 rcases akraBazzi_increment_lower_multi hvalid hnonempty hp hq hsmooth with ⟨ε, hε, n₁, hinc⟩ have hT_nonneg : ∀ n, 0 ≤ T n := akraBazzi_T_nonneg hvalid hgnonneg hsat let Tsum : ℝ := ∑ m ∈ Finset.range (n₁ + 1), T m let C : ℝ := 1 + ε⁻¹ + Tsum have hC_pos : 0 < C := by dsimp [C] have hε_inv : 0 < ε⁻¹ := inv_pos.mpr hε have hTsum : 0 ≤ Tsum := by dsimp [Tsum] exact Finset.sum_nonneg (by intro m _hm; exact hT_nonneg m) positivity have hC_ge_one : 1 ≤ C := by dsimp [C] have hε_inv : 0 ≤ ε⁻¹ := le_of_lt (inv_pos.mpr hε) have hTsum : 0 ≤ Tsum := by dsimp [Tsum] exact Finset.sum_nonneg (by intro m _hm; exact hT_nonneg m) linarith have hC_ge_inv : ε⁻¹ ≤ C := by dsimp [C] have hTsum : 0 ≤ Tsum := by dsimp [Tsum] exact Finset.sum_nonneg (by intro m _hm; exact hT_nonneg m) linarith have hmain : ∀ n, T n ≤ C * akraBazziScale p g n := by intro n induction n using Nat.strong_induction_on with | h n ih => by_cases hn0 : n = 0 · subst n rw [hsat.1] exact mul_nonneg hC_pos.le (akraBazziScale_nonneg hp hgnonneg 0) · have hn_pos : 0 < n := Nat.pos_of_ne_zero hn0 have hn_pos' : 1 ≤ n := Nat.succ_le_iff.mpr hn_pos by_cases hle : n ≤ n₀ · -- base case have hF1 : 1 ≤ akraBazziScale p g n := akraBazzi_scale_ge_one hp hgnonneg hn_pos' calc T n = 1 := hsat.2.1 n hn_pos' hle _ ≤ C := hC_ge_one _ ≤ C * akraBazziScale p g n := by calc C = C * 1 := by rw [mul_one] _ ≤ C * akraBazziScale p g n := mul_le_mul_of_nonneg_left hF1 hC_pos.le · have hn₀n : n₀ < n := lt_of_not_ge hle by_cases hsmall : n ≤ n₁ · -- finite initial range: T n ≤ C, and 1 ≤ F n have hTn_le : T n ≤ C := by have hsum_dom : T n ≤ Tsum := by dsimp [Tsum] have hn_mem : n ∈ Finset.range (n₁ + 1) := by rw [Finset.mem_range] exact Nat.lt_succ_of_le hsmall exact Finset.single_le_sum (by intro m _hm; exact hT_nonneg m) hn_mem have hε_inv_nonneg : 0 ≤ ε⁻¹ := le_of_lt (inv_pos.mpr hε) dsimp [C] linarith have hF1 : 1 ≤ akraBazziScale p g n := akraBazzi_scale_ge_one hp hgnonneg hn_pos' calc T n ≤ C := hTn_le _ ≤ C * akraBazziScale p g n := by calc C = C * 1 := by rw [mul_one] _ ≤ C * akraBazziScale p g n := mul_le_mul_of_nonneg_left hF1 hC_pos.le · -- main induction step have hn₁n : n₁ ≤ n := le_of_not_ge hsmall have hIH : ∀ ab ∈ branches, T (⌊(n : ℝ) / ab.2⌋₊) ≤ C * akraBazziScale p g (⌊(n : ℝ) / ab.2⌋₊) := by intro ab hab have hvalid_ab : BranchValid ab := hvalid ab hab have hlt : ⌊(n : ℝ) / ab.2⌋₊ < n := floor_div_lt_self hvalid_ab.2 hn_pos exact ih (⌊(n : ℝ) / ab.2⌋₊) hlt have hsum_le : (branches.map (fun ab => (ab.1 : ℝ) * T (⌊(n : ℝ) / ab.2⌋₊))).sum ≤ C * (branches.map (fun ab => (ab.1 : ℝ) * ((n : ℝ) / ab.2) ^ p * (1 + akraBazziIntegral p g (⌊(n : ℝ) / ab.2⌋₊)))).sum := by calc (branches.map (fun ab => (ab.1 : ℝ) * T (⌊(n : ℝ) / ab.2⌋₊))).sum ≤ (branches.map (fun ab => (ab.1 : ℝ) * (C * akraBazziScale p g (⌊(n : ℝ) / ab.2⌋₊)))).sum := by apply List.sum_le_sum intro ab hab exact mul_le_mul_of_nonneg_left (hIH ab hab) (Nat.cast_nonneg ab.1) _ = C * (branches.map (fun ab => (ab.1 : ℝ) * akraBazziScale p g (⌊(n : ℝ) / ab.2⌋₊))).sum := by rw [show (branches.map (fun ab => (ab.1 : ℝ) * (C * akraBazziScale p g (⌊(n : ℝ) / ab.2⌋₊)))).sum = C * (branches.map (fun ab => (ab.1 : ℝ) * akraBazziScale p g (⌊(n : ℝ) / ab.2⌋₊))).sum by calc (branches.map (fun ab => (ab.1 : ℝ) * (C * akraBazziScale p g (⌊(n : ℝ) / ab.2⌋₊)))).sum = (branches.map (fun ab => C * ((ab.1 : ℝ) * akraBazziScale p g (⌊(n : ℝ) / ab.2⌋₊)))).sum := by exact congrArg List.sum (List.map_congr_left (by intro ab _hab; ring)) _ = C * (branches.map (fun ab => (ab.1 : ℝ) * akraBazziScale p g (⌊(n : ℝ) / ab.2⌋₊))).sum := by rw [List.sum_map_mul_left branches (fun ab => (ab.1 : ℝ) * akraBazziScale p g (⌊(n : ℝ) / ab.2⌋₊)) C]] _ ≤ C * (branches.map (fun ab => (ab.1 : ℝ) * ((n : ℝ) / ab.2) ^ p * (1 + akraBazziIntegral p g (⌊(n : ℝ) / ab.2⌋₊)))).sum := by apply mul_le_mul_of_nonneg_left _ hC_pos.le apply List.sum_le_sum intro ab hab have hvalid_ab : BranchValid ab := hvalid ab hab unfold akraBazziScale have hfloor : (⌊(n : ℝ) / ab.2⌋₊ : ℝ) ≤ (n : ℝ) / ab.2 := Nat.floor_le (le_of_lt (div_pos (by exact_mod_cast hn_pos) (lt_trans (by norm_num : (0 : ℝ) < 1) hvalid_ab.2))) have hpow : (⌊(n : ℝ) / ab.2⌋₊ : ℝ) ^ p ≤ ((n : ℝ) / ab.2) ^ p := Real.rpow_le_rpow (by positivity) hfloor hp have hnonneg : 0 ≤ 1 + akraBazziIntegral p g (⌊(n : ℝ) / ab.2⌋₊) := add_nonneg (by norm_num) (akraBazziIntegral_nonneg hgnonneg _) simpa [mul_assoc] using mul_le_mul_of_nonneg_left (mul_le_mul_of_nonneg_right hpow hnonneg) (Nat.cast_nonneg ab.1) have hdecomp := akraBazzi_scale_decomp branches p g n hvalid hroot have hinc_bound : ε * g n ≤ (branches.map (fun ab => akraBazziIncrement p g ab n)).sum := hinc n hn₁n calc T n = (branches.map (fun ab => (ab.1 : ℝ) * T (⌊(n : ℝ) / ab.2⌋₊))).sum + g n := hsat.2.2 n hn₀n _ ≤ C * (branches.map (fun ab => (ab.1 : ℝ) * ((n : ℝ) / ab.2) ^ p * (1 + akraBazziIntegral p g (⌊(n : ℝ) / ab.2⌋₊)))).sum + g n := add_le_add_left hsum_le (g n) _ = C * (akraBazziScale p g n - (branches.map (fun ab => akraBazziIncrement p g ab n)).sum) + g n := by rw [hdecomp] _ = C * akraBazziScale p g n - C * (branches.map (fun ab => akraBazziIncrement p g ab n)).sum + g n := by ring _ ≤ C * akraBazziScale p g n - C * (ε * g n) + g n := by exact add_le_add_left (sub_le_sub_left (mul_le_mul_of_nonneg_left hinc_bound hC_pos.le) (C * akraBazziScale p g n)) (g n) _ = C * akraBazziScale p g n - (C * ε - 1) * g n := by ring _ ≤ C * akraBazziScale p g n := by have hCeps : 1 ≤ C * ε := by have : ε⁻¹ * ε ≤ C * ε := mul_le_mul_of_nonneg_right hC_ge_inv (le_of_lt hε) have hεε : ε⁻¹ * ε = 1 := inv_mul_cancel₀ (ne_of_gt hε) simpa [hεε] using this have hnonneg_g : 0 ≤ g n := hgnonneg n have hCeps1 : 0 ≤ C * ε - 1 := by linarith have hsub : (C * ε - 1) * g n ≥ 0 := mul_nonneg hCeps1 hnonneg_g linarith rw [Chapter03.isBigO_iff] refine ⟨C, hC_pos, 0, ?_⟩ intro n hn rw [abs_of_nonneg (hT_nonneg n)] rw [abs_of_nonneg (akraBazziScale_nonneg hp hgnonneg n)] exact hmain n

The positive-root specialization of the upper recurrence comparison.

theorem akraBazzi_upper_bound {branches : List (ℕ × ℝ)} {g T : ℕ → ℝ} {n₀ : ℕ} {p q : ℝ} (hvalid : BranchesValid branches) (hnonempty : branches ≠ []) (hroot : IsAkraBazziRoot branches p) (hp : 0 < p) (hq : 0 ≤ q) (hsmooth : PolynomialGrowth g q) (hsat : SatisfiesAkraBazzi branches g T n₀) : Chapter03.isBigO T (akraBazziScale p g) := akraBazzi_upper_bound_nonneg hvalid hnonempty hroot hp.le hq hsmooth hsat

The lower comparison

Above the base threshold, a solution of an Akra–Bazzi recurrence is at least the forcing term: the recursion tree only adds nonnegative work on top of g n.

lemma akraBazzi_T_ge_g {branches : List (ℕ × ℝ)} {g T : ℕ → ℝ} {n₀ : ℕ} (hvalid : BranchesValid branches) (hgnonneg : ∀ n, 0 ≤ g n) (hsat : SatisfiesAkraBazzi branches g T n₀) : ∀ n, n₀ < n → g n ≤ T n := by intro n hn₀n have hT_nonneg : ∀ n, 0 ≤ T n := akraBazzi_T_nonneg hvalid hgnonneg hsat rw [hsat.2.2 n hn₀n] have hsum_nonneg : 0 ≤ (branches.map (fun ab => (ab.1 : ℝ) * T (⌊(n : ℝ) / ab.2⌋₊))).sum := by apply List.sum_nonneg intro x hx rw [List.mem_map] at hx rcases hx with ⟨ab, hab, rfl⟩ exact mul_nonneg (Nat.cast_nonneg ab.1) (hT_nonneg _) linarith

For p + 1 ≤ q, the integral is bounded by a polynomial C n^(q-p).

lemma akraBazzi_integral_le_poly {p q : ℝ} {g : ℕ → ℝ} (hsmooth : PolynomialGrowth g q) (hpq : p + 1 ≤ q) : ∃ C : ℝ, 0 < C ∧ ∀ n, akraBazziIntegral p g n ≤ C * (n : ℝ) ^ (q - p) := by rcases hsmooth with ⟨_hgnonneg, _hgmono, _c, Cg, _hcpos, hCpos, _hglower, hgupper⟩ have hqmp : 0 ≤ q - p - 1 := by linarith refine ⟨Cg, hCpos, ?_⟩ intro n unfold akraBazziIntegral calc (∑ u ∈ Finset.range n, g (u + 1) / ((u + 1 : ℕ) : ℝ) ^ (p + 1)) ≤ ∑ u ∈ Finset.range n, Cg * ((u + 1 : ℕ) : ℝ) ^ (q - p - 1) := by apply Finset.sum_le_sum intro u _hu have hu1 : 1 ≤ u + 1 := by omega have hg : g (u + 1) ≤ Cg * ((u + 1 : ℕ) : ℝ) ^ q := hgupper (u + 1) hu1 calc g (u + 1) / ((u + 1 : ℕ) : ℝ) ^ (p + 1) ≤ Cg * ((u + 1 : ℕ) : ℝ) ^ q / ((u + 1 : ℕ) : ℝ) ^ (p + 1) := div_le_div_of_nonneg_right hg (Real.rpow_nonneg (by positivity) (p + 1)) _ = Cg * ((u + 1 : ℕ) : ℝ) ^ (q - p - 1) := by rw [show q - p - 1 = q - (p + 1) by ring] rw [show Cg * ((u + 1 : ℕ) : ℝ) ^ q / ((u + 1 : ℕ) : ℝ) ^ (p + 1) = Cg * (((u + 1 : ℕ) : ℝ) ^ q / ((u + 1 : ℕ) : ℝ) ^ (p + 1)) by ring] rw [show ((u + 1 : ℕ) : ℝ) ^ q / ((u + 1 : ℕ) : ℝ) ^ (p + 1) = ((u + 1 : ℕ) : ℝ) ^ (q - (p + 1)) by exact (Real.rpow_sub (by exact_mod_cast (Nat.succ_pos u) : 0 < ((u+1:ℕ):ℝ)) q (p + 1)).symm] _ ≤ ∑ u ∈ Finset.range n, Cg * (n : ℝ) ^ (q - p - 1) := by apply Finset.sum_le_sum intro u _hu have hu_le : (u + 1 : ℕ) ≤ n := by have : u < n := by simpa [Finset.mem_range] using _hu omega exact mul_le_mul_of_nonneg_left (Real.rpow_le_rpow (by positivity) (by exact_mod_cast hu_le) hqmp) hCpos.le _ = Cg * (n : ℝ) ^ (q - p - 1) * (n : ℕ) := by rw [Finset.sum_const, nsmul_eq_mul, Finset.card_range] ring _ = Cg * (n : ℝ) ^ (q - p) := by have hpow : (n : ℝ) ^ (q - p - 1) * (n : ℝ) = (n : ℝ) ^ (q - p) := by rw [show (n : ℝ) ^ (q - p) = (n : ℝ) ^ ((q - p - 1) + 1) by congr 1; ring] rw [Real.rpow_add' (Nat.cast_nonneg n) (by linarith [hpq] : (q - p - 1) + 1 ≠ 0)] rw [Real.rpow_one] rw [show Cg * (n : ℝ) ^ (q - p - 1) * (n : ℕ) = Cg * ((n : ℝ) ^ (q - p - 1) * (n : ℝ)) by ring] rw [hpow]

For 2 ≤ x and 0 < p, the power drop from x to ⌊x⌋₊ is O(x^(p-1)): a one-step mean-value bound on the monotone power map.

lemma rpow_sub_floor_le_of_two_le {p : ℝ} (hp : 0 < p) {x : ℝ} (hx2 : 2 ≤ x) : x ^ p - (⌊x⌋₊ : ℝ) ^ p ≤ 2 * (1 + p) * x ^ (p - 1) := by have hx0 : 0 < x := lt_of_lt_of_le (by norm_num) hx2 by_cases hx_int : (⌊x⌋₊ : ℝ) = x · rw [hx_int] have hnonneg : 0 ≤ 2 * (1 + p) * x ^ (p - 1) := by positivity linarith · have hfloor_le : (⌊x⌋₊ : ℝ) ≤ x := Nat.floor_le hx0.le have hfloor_ge : x - 1 ≤ (⌊x⌋₊ : ℝ) := by have h := Nat.lt_floor_add_one x linarith have hfloor_ge_half : x / 2 ≤ (⌊x⌋₊ : ℝ) := by have h1 : x / 2 ≤ x - 1 := by linarith exact le_trans h1 hfloor_ge have hfloor_ge_one : 1 ≤ (⌊x⌋₊ : ℝ) := by have h1 : (1 : ℝ) ≤ x / 2 := by linarith exact le_trans h1 hfloor_ge_half have hlt : (⌊x⌋₊ : ℝ) < x := lt_of_le_of_ne hfloor_le hx_int have hmvt : ∃ c ∈ Set.Ioo (⌊x⌋₊ : ℝ) x, deriv (fun z : ℝ => z ^ p) c = (x ^ p - (⌊x⌋₊ : ℝ) ^ p) / (x - (⌊x⌋₊ : ℝ)) := by refine exists_deriv_eq_slope (fun z : ℝ => z ^ p) hlt ?_ ?_ · exact (Real.continuous_rpow_const hp.le).continuousOn.mono (Set.subset_univ _) · intro z hz rw [Set.mem_Ioo] at hz have hz_pos : 0 < z := lt_of_lt_of_le (by norm_num : (0 : ℝ) < 1) (le_trans hfloor_ge_one (le_of_lt hz.1)) exact (Real.differentiableAt_rpow_const_of_ne p (ne_of_gt hz_pos)).differentiableWithinAt rcases hmvt with ⟨c, hcIoo, hc⟩ have hc_pos : 0 < c := by rw [Set.mem_Ioo] at hcIoo exact lt_of_lt_of_le (by norm_num : (0 : ℝ) < 1) (le_trans hfloor_ge_one (le_of_lt hcIoo.1)) have hc_le_x : c ≤ x := by rw [Set.mem_Ioo] at hcIoo exact le_of_lt hcIoo.2 have hc_ge_half : x / 2 ≤ c := by rw [Set.mem_Ioo] at hcIoo exact le_trans hfloor_ge_half (le_of_lt hcIoo.1) have hden_pos : 0 < x - (⌊x⌋₊ : ℝ) := sub_pos.mpr hlt have hden_le_one : x - (⌊x⌋₊ : ℝ) ≤ 1 := by have h := Nat.lt_floor_add_one x linarith have hmain : x ^ p - (⌊x⌋₊ : ℝ) ^ p = p * c ^ (p - 1) * (x - (⌊x⌋₊ : ℝ)) := by rw [Real.deriv_rpow_const] at hc rw [eq_div_iff (ne_of_gt hden_pos)] at hc exact hc.symm have hcp : c ^ (p - 1) ≤ 2 * x ^ (p - 1) := by by_cases hp1 : 1 ≤ p · have h : 0 ≤ p - 1 := sub_nonneg.mpr hp1 calc c ^ (p - 1) ≤ x ^ (p - 1) := Real.rpow_le_rpow hc_pos.le hc_le_x h _ ≤ 2 * x ^ (p - 1) := by have hx_p1_nonneg : 0 ≤ x ^ (p - 1) := Real.rpow_nonneg hx0.le (p - 1) nlinarith · have hp_lt_one : p < 1 := lt_of_not_ge hp1 have hneg : p - 1 < 0 := sub_neg.mpr hp_lt_one have hx_half_pos : 0 < x / 2 := by positivity have hhalf : (x / 2) ^ (p - 1) = (2 : ℝ) ^ (1 - p) * x ^ (p - 1) := by rw [Real.div_rpow hx0.le (by norm_num : 0 ≤ (2 : ℝ))] rw [div_eq_mul_inv] rw [← Real.rpow_neg (by norm_num : 0 ≤ (2 : ℝ)) (p - 1)] rw [show -(p - 1) = 1 - p by ring] ring calc c ^ (p - 1) ≤ (x / 2) ^ (p - 1) := Real.rpow_le_rpow_of_nonpos hx_half_pos hc_ge_half (le_of_lt hneg) _ = (2 : ℝ) ^ (1 - p) * x ^ (p - 1) := hhalf _ ≤ 2 * x ^ (p - 1) := by have h2 : (2 : ℝ) ^ (1 - p) ≤ 2 := by have hsub : 1 - p ≤ 1 := by nlinarith have hle : (2 : ℝ) ^ (1 - p) ≤ (2 : ℝ) ^ (1 : ℝ) := Real.rpow_le_rpow_of_exponent_le (by norm_num : (1 : ℝ) ≤ 2) hsub rw [Real.rpow_one] at hle exact hle exact mul_le_mul_of_nonneg_right h2 (Real.rpow_nonneg hx0.le (p - 1)) calc x ^ p - (⌊x⌋₊ : ℝ) ^ p = p * c ^ (p - 1) * (x - (⌊x⌋₊ : ℝ)) := hmain _ ≤ p * c ^ (p - 1) * 1 := by have hnonneg : 0 ≤ p * c ^ (p - 1) := mul_nonneg hp.le (Real.rpow_nonneg hc_pos.le (p - 1)) exact mul_le_mul_of_nonneg_left hden_le_one hnonneg _ = p * c ^ (p - 1) := by ring _ ≤ p * (2 * x ^ (p - 1)) := mul_le_mul_of_nonneg_left hcp hp.le _ = 2 * p * x ^ (p - 1) := by ring _ ≤ 2 * (1 + p) * x ^ (p - 1) := by have hx_p1_nonneg : 0 ≤ x ^ (p - 1) := Real.rpow_nonneg hx0.le (p - 1) nlinarith

The summed power floor loss Σᵢ aᵢ ((n/bᵢ)^p - ⌊n/bᵢ⌋^p) is at most a constant multiple of n^(p-1). This is the sub-leading loss of rounding the subproblem size down; it is what the driving term absorbs in the critical regime.

lemma akraBazzi_power_floor_loss {branches : List (ℕ × ℝ)} {p : ℝ} (hvalid : BranchesValid branches) (hp : 0 < p) : ∃ C : ℝ, 0 < C ∧ ∃ n₀ : ℕ, ∀ n, n₀ ≤ n → (branches.map (fun ab => (ab.1 : ℝ) * (((n : ℝ) / ab.2) ^ p - (⌊(n : ℝ) / ab.2⌋₊ : ℝ) ^ p))).sum ≤ C * (n : ℝ) ^ (p - 1) := by let C : ℝ := (branches.map (fun ab => (ab.1 : ℝ) * (2 * (1 + p) * ab.2 ^ (1 - p)))).sum + 1 have hC_pos : 0 < C := by dsimp [C] have hsum_nonneg : 0 ≤ (branches.map (fun ab => (ab.1 : ℝ) * (2 * (1 + p) * ab.2 ^ (1 - p)))).sum := by apply List.sum_nonneg intro x hx rw [List.mem_map] at hx rcases hx with ⟨ab, hab, rfl⟩ have hvalid_ab : BranchValid ab := hvalid ab hab have hb_pos : 0 < ab.2 := lt_trans (by norm_num : (0 : ℝ) < 1) hvalid_ab.2 exact mul_nonneg (Nat.cast_nonneg ab.1) (by positivity) linarith let n₀ : ℕ := (branches.map (fun ab => Nat.ceil (2 * ab.2) + 1)).foldr max 0 refine ⟨C, hC_pos, n₀, ?_⟩ intro n hn have hsum_le : (branches.map (fun ab => (ab.1 : ℝ) * (((n : ℝ) / ab.2) ^ p - (⌊(n : ℝ) / ab.2⌋₊ : ℝ) ^ p))).sum ≤ (branches.map (fun ab => (ab.1 : ℝ) * (2 * (1 + p) * ab.2 ^ (1 - p) * (n : ℝ) ^ (p - 1)))).sum := by apply List.sum_le_sum intro ab hab have hvalid_ab : BranchValid ab := hvalid ab hab have hb_pos : 0 < ab.2 := lt_trans (by norm_num : (0 : ℝ) < 1) hvalid_ab.2 have hx_threshold : Nat.ceil (2 * ab.2) + 1 ≤ n := by have hx_in_fold : Nat.ceil (2 * ab.2) + 1 ≤ n₀ := by dsimp [n₀] exact List_foldr_max_ge_mem (by rw [List.mem_map] exact ⟨ab, hab, rfl⟩) exact le_trans hx_in_fold hn have hn_2b : 2 * ab.2 ≤ (n : ℝ) := by have hceil : 2 * ab.2 ≤ (Nat.ceil (2 * ab.2) : ℝ) := Nat.le_ceil (2 * ab.2) have hn' : (Nat.ceil (2 * ab.2) + 1 : ℝ) ≤ (n : ℝ) := by exact_mod_cast hx_threshold have hceil1 : (Nat.ceil (2 * ab.2) : ℝ) ≤ (Nat.ceil (2 * ab.2) + 1 : ℝ) := by norm_num linarith have hn_b : 2 ≤ (n : ℝ) / ab.2 := by rw [le_div_iff₀ hb_pos] simpa [mul_comm] using hn_2b have hfloor := rpow_sub_floor_le_of_two_le hp hn_b have hpow : ((n : ℝ) / ab.2) ^ (p - 1) = ab.2 ^ (1 - p) * (n : ℝ) ^ (p - 1) := by rw [Real.div_rpow (Nat.cast_nonneg n) hb_pos.le] rw [div_eq_mul_inv] rw [← Real.rpow_neg hb_pos.le (p - 1)] rw [show -(p - 1) = 1 - p by ring] ring have hnonneg_a : 0 ≤ (ab.1 : ℝ) := Nat.cast_nonneg ab.1 calc (ab.1 : ℝ) * (((n : ℝ) / ab.2) ^ p - (⌊(n : ℝ) / ab.2⌋₊ : ℝ) ^ p) ≤ (ab.1 : ℝ) * (2 * (1 + p) * ((n : ℝ) / ab.2) ^ (p - 1)) := mul_le_mul_of_nonneg_left hfloor hnonneg_a _ = (ab.1 : ℝ) * (2 * (1 + p) * (ab.2 ^ (1 - p) * (n : ℝ) ^ (p - 1))) := by rw [hpow] _ = (ab.1 : ℝ) * (2 * (1 + p) * ab.2 ^ (1 - p) * (n : ℝ) ^ (p - 1)) := by ring calc (branches.map (fun ab => (ab.1 : ℝ) * (((n : ℝ) / ab.2) ^ p - (⌊(n : ℝ) / ab.2⌋₊ : ℝ) ^ p))).sum ≤ (branches.map (fun ab => (ab.1 : ℝ) * (2 * (1 + p) * ab.2 ^ (1 - p) * (n : ℝ) ^ (p - 1)))).sum := hsum_le _ = (branches.map (fun ab => (ab.1 : ℝ) * (2 * (1 + p) * ab.2 ^ (1 - p)))).sum * (n : ℝ) ^ (p - 1) := by rw [show (branches.map (fun ab => (ab.1 : ℝ) * (2 * (1 + p) * ab.2 ^ (1 - p) * (n : ℝ) ^ (p - 1)))).sum = (branches.map (fun ab => ((ab.1 : ℝ) * (2 * (1 + p) * ab.2 ^ (1 - p))) * (n : ℝ) ^ (p - 1))).sum by exact congrArg List.sum (List.map_congr_left (by intro ab _hab; ring))] rw [List.sum_map_mul_right branches (fun ab => (ab.1 : ℝ) * (2 * (1 + p) * ab.2 ^ (1 - p))) ((n : ℝ) ^ (p - 1))] _ ≤ C * (n : ℝ) ^ (p - 1) := by have hpow_nonneg : 0 ≤ (n : ℝ) ^ (p - 1) := Real.rpow_nonneg (Nat.cast_nonneg n) (p - 1) dsimp [C] nlinarith

The Akra–Bazzi scale factor 1 + I n is at most a constant multiple of n when the driving function has polynomial growth of exponent p > 0.

lemma akraBazzi_scale_factor_le_linear {p : ℝ} {g : ℕ → ℝ} (Variable name `hp` is not explicitly referenced. The binding can be removed (if unused) or named `_` (if used implicitly). Note: This linter can be disabled with `set_option linter.unusedVariables false`hp : 0 < p) (C : ℝ) (hC : 0 < C) (hgupper : ∀ n, 1 ≤ n → g n ≤ C * (n : ℝ) ^ p) : ∃ C' : ℝ, 0 < C' ∧ ∀ n, 1 ≤ n → 1 + akraBazziIntegral p g n ≤ C' * (n : ℝ) := by refine ⟨C + 1, by positivity, ?_⟩ intro n hn have hI : akraBazziIntegral p g n ≤ C * (n : ℝ) := by unfold akraBazziIntegral calc (∑ u ∈ Finset.range n, g (u + 1) / ((u + 1 : ℕ) : ℝ) ^ (p + 1)) ≤ ∑ u ∈ Finset.range n, C := by apply Finset.sum_le_sum intro u _hu have hu1 : 1 ≤ u + 1 := by omega have hg : g (u + 1) ≤ C * ((u + 1 : ℕ) : ℝ) ^ p := hgupper (u + 1) hu1 calc g (u + 1) / ((u + 1 : ℕ) : ℝ) ^ (p + 1) ≤ C * ((u + 1 : ℕ) : ℝ) ^ p / ((u + 1 : ℕ) : ℝ) ^ (p + 1) := div_le_div_of_nonneg_right hg (Real.rpow_nonneg (by positivity) (p + 1)) _ ≤ C := by have hu1' : 1 ≤ ((u + 1 : ℕ) : ℝ) := by exact_mod_cast hu1 have hratio : ((u + 1 : ℕ) : ℝ) ^ p / ((u + 1 : ℕ) : ℝ) ^ (p + 1) ≤ 1 := by rw [← Real.rpow_sub (by positivity : 0 < ((u + 1 : ℕ) : ℝ)) p (p + 1)] rw [show p - (p + 1) = -1 by ring] exact Real.rpow_le_one_of_one_le_of_nonpos hu1' (by norm_num : (-1 : ℝ) ≤ 0) calc C * ((u + 1 : ℕ) : ℝ) ^ p / ((u + 1 : ℕ) : ℝ) ^ (p + 1) = C * (((u + 1 : ℕ) : ℝ) ^ p / ((u + 1 : ℕ) : ℝ) ^ (p + 1)) := by ring _ ≤ C * 1 := mul_le_mul_of_nonneg_left hratio hC.le _ = C := by ring _ = C * (n : ℝ) := by rw [Finset.sum_const, nsmul_eq_mul, Finset.card_range] ring have h1I : 1 + akraBazziIntegral p g n ≤ (C + 1) * (n : ℝ) := by have hn' : 1 ≤ (n : ℝ) := by exact_mod_cast hn nlinarith [hI, hn'] exact h1I

Akra–Bazzi lower bound (forcing-dominated regime). When the driving exponent q strictly exceeds the root p by at least one (so p + 1 ≤ q), the forcing g dominates the recursion tree: T(n) = Ω(n^q), while the scale n^p (1 + I n) = O(n^q), giving T(n) = Ω(n^p (1 + I n)).

theorem akraBazzi_lower_bound {branches : List (ℕ × ℝ)} {g T : ℕ → ℝ} {n₀ : ℕ} {p q : ℝ} (hvalid : BranchesValid branches) (Variable name `hnonempty` is not explicitly referenced. The binding can be removed (if unused) or named `_` (if used implicitly). Note: This linter can be disabled with `set_option linter.unusedVariables false`hnonempty : branches ≠ []) (_hroot : IsAkraBazziRoot branches p) (hp : 0 < p) (hpq : p + 1 ≤ q) (hsmooth : PolynomialGrowth g q) (hsat : SatisfiesAkraBazzi branches g T n₀) : Chapter03.isBigOmega T (akraBazziScale p g) := by have hgnonneg : ∀ n, 0 ≤ g n := hsmooth.1 rcases hsmooth.2.2 with ⟨c₀, _C, hc₀pos, _hCpos, hglower, _hgupper⟩ rcases akraBazzi_integral_le_poly hsmooth hpq with ⟨Ci, _hCipos, hCi⟩ have hT_nonneg : ∀ n, 0 ≤ T n := akraBazzi_T_nonneg hvalid hgnonneg hsat have hT_ge_g : ∀ n, n₀ < n → g n ≤ T n := akraBazzi_T_ge_g hvalid hgnonneg hsat let c : ℝ := c₀ / (1 + Ci) have hc_pos : 0 < c := div_pos hc₀pos (by positivity) rw [Chapter03.isBigOmega_iff] refine ⟨c, hc_pos, n₀ + 1, ?_⟩ intro n hn have hn₀n : n₀ < n := by omega have hn1 : 1 ≤ n := by omega have hg_lower : c₀ * (n : ℝ) ^ q ≤ g n := hglower n hn1 have hF_le : akraBazziScale p g n ≤ (1 + Ci) * (n : ℝ) ^ q := by unfold akraBazziScale have hI : akraBazziIntegral p g n ≤ Ci * (n : ℝ) ^ (q - p) := hCi n have hnp : (n : ℝ) ^ p ≤ (n : ℝ) ^ q := by have hn' : 1 ≤ (n : ℝ) := by exact_mod_cast hn1 exact Real.rpow_le_rpow_of_exponent_le hn' (by linarith [hpq] : p ≤ q) calc (n : ℝ) ^ p * (1 + akraBazziIntegral p g n) ≤ (n : ℝ) ^ p * (1 + Ci * (n : ℝ) ^ (q - p)) := mul_le_mul_of_nonneg_left (by linarith) (Real.rpow_nonneg (by positivity) p) _ = (n : ℝ) ^ p + Ci * ((n : ℝ) ^ p * (n : ℝ) ^ (q - p)) := by ring _ = (n : ℝ) ^ p + Ci * (n : ℝ) ^ q := by have hn_pos : 0 < (n : ℝ) := by exact_mod_cast (lt_of_lt_of_le (by norm_num : (0:ℕ)<1) hn1) rw [show (n : ℝ) ^ p * (n : ℝ) ^ (q - p) = (n : ℝ) ^ q by rw [← Real.rpow_add hn_pos p (q - p)] congr 1; ring] _ ≤ (n : ℝ) ^ q + Ci * (n : ℝ) ^ q := by nlinarith [hnp] _ = (1 + Ci) * (n : ℝ) ^ q := by ring rw [abs_of_nonneg (akraBazziScale_nonneg hp.le hgnonneg n)] rw [abs_of_nonneg (hT_nonneg n)] calc c * akraBazziScale p g n ≤ c * ((1 + Ci) * (n : ℝ) ^ q) := mul_le_mul_of_nonneg_left hF_le hc_pos.le _ = c₀ * (n : ℝ) ^ q := by dsimp [c] field_simp [ne_of_gt (by positivity : 0 < 1 + Ci)] _ ≤ g n := hg_lower _ ≤ T n := hT_ge_g n hn₀n

The scale at the floored subproblem sizes decomposes as the scale minus the increment minus the power floor loss: Σᵢ aᵢ (⌊n/bᵢ⌋)^p (1 + I ⌊n/bᵢ⌋) equals n^p (1 + I n) minus the increment akraBazziIncrement and minus the power rounding loss. This is the lower-bound counterpart of akraBazzi_scale_decomp.

lemma akraBazzi_scale_floor_decomp (branches : List (ℕ × ℝ)) (p : ℝ) (g : ℕ → ℝ) (n : ℕ) (hvalid : BranchesValid branches) (hroot : IsAkraBazziRoot branches p) : (branches.map (fun ab => (ab.1 : ℝ) * akraBazziScale p g (⌊(n : ℝ) / ab.2⌋₊))).sum = akraBazziScale p g n - (branches.map (fun ab => akraBazziIncrement p g ab n)).sum - (branches.map (fun ab => (ab.1 : ℝ) * (((n : ℝ) / ab.2) ^ p - (⌊(n : ℝ) / ab.2⌋₊ : ℝ) ^ p) * (1 + akraBazziIntegral p g (⌊(n : ℝ) / ab.2⌋₊)))).sum := by unfold akraBazziScale have hsplit : (branches.map (fun ab => (ab.1 : ℝ) * ((⌊(n : ℝ) / ab.2⌋₊ : ℝ) ^ p * (1 + akraBazziIntegral p g (⌊(n : ℝ) / ab.2⌋₊))))).sum + (branches.map (fun ab => (ab.1 : ℝ) * (((n : ℝ) / ab.2) ^ p - (⌊(n : ℝ) / ab.2⌋₊ : ℝ) ^ p) * (1 + akraBazziIntegral p g (⌊(n : ℝ) / ab.2⌋₊)))).sum = (branches.map (fun ab => (ab.1 : ℝ) * ((n : ℝ) / ab.2) ^ p * (1 + akraBazziIntegral p g (⌊(n : ℝ) / ab.2⌋₊)))).sum := by rw [← List.sum_map_add] exact congrArg List.sum (List.map_congr_left (by intro ab _hab; ring)) have hsplit' : (branches.map (fun ab => (ab.1 : ℝ) * ((⌊(n : ℝ) / ab.2⌋₊ : ℝ) ^ p * (1 + akraBazziIntegral p g (⌊(n : ℝ) / ab.2⌋₊))))).sum = (branches.map (fun ab => (ab.1 : ℝ) * ((n : ℝ) / ab.2) ^ p * (1 + akraBazziIntegral p g (⌊(n : ℝ) / ab.2⌋₊)))).sum - (branches.map (fun ab => (ab.1 : ℝ) * (((n : ℝ) / ab.2) ^ p - (⌊(n : ℝ) / ab.2⌋₊ : ℝ) ^ p) * (1 + akraBazziIntegral p g (⌊(n : ℝ) / ab.2⌋₊)))).sum := by linarith rw [hsplit'] rw [akraBazzi_scale_decomp branches p g n hvalid hroot] unfold akraBazziScale akraBazziIncrement ring

Akra–Bazzi lower bound (critical regime). When the driving exponent q equals the root p, the solution is T(n) = Ω(n^p (1 + I n)). This completes the critical case of the lower comparison: the power floor loss is sub-leading, absorbed by the driving term g n together with the increment bound akraBazzi_increment_upper.

theorem akraBazzi_lower_bound_critical {branches : List (ℕ × ℝ)} {g T : ℕ → ℝ} {n₀ : ℕ} {p : ℝ} (hvalid : BranchesValid branches) (hnonempty : branches ≠ []) (hroot : IsAkraBazziRoot branches p) (hp : 0 < p) (hsmooth : PolynomialGrowth g p) (hsat : SatisfiesAkraBazzi branches g T n₀) : Chapter03.isBigOmega T (akraBazziScale p g) := by have hgnonneg : ∀ n, 0 ≤ g n := hsmooth.1 have hgmono : ∀ {m n : ℕ}, m ≤ n → g m ≤ g n := hsmooth.2.1 rcases hsmooth.2.2 with ⟨c₀, C₀, hc₀pos, hC₀pos, hglower, hgupper⟩ rcases akraBazzi_increment_upper hvalid hnonempty hp.le hgnonneg hgmono with ⟨K, hKpos, n₁, hinc⟩ rcases akraBazzi_power_floor_loss hvalid hp with ⟨Cpl, hCplpos, n₂, hpl⟩ rcases akraBazzi_scale_factor_le_linear hp C₀ hC₀pos hgupper with ⟨Cint, hCintpos, hInt⟩ let M : ℝ := Cpl * Cint / c₀ have hMpos : 0 < M := by dsimp [M]; positivity let N₀ : ℕ := max n₀ (max n₁ n₂) + 1 have hEloss : ∀ n, N₀ ≤ n → (branches.map (fun ab => (ab.1 : ℝ) * (((n : ℝ) / ab.2) ^ p - (⌊(n : ℝ) / ab.2⌋₊ : ℝ) ^ p) * (1 + akraBazziIntegral p g (⌊(n : ℝ) / ab.2⌋₊)))).sum ≤ M * g n := by intro n hn have hn₂ : n₂ ≤ n := le_trans (Nat.le_max_right n₁ n₂) (by omega) have hn_pos : 1 ≤ n := by omega have hI_bound : 1 + akraBazziIntegral p g n ≤ Cint * (n : ℝ) := hInt n hn_pos have hpln : (branches.map (fun ab => (ab.1 : ℝ) * (((n : ℝ) / ab.2) ^ p - (⌊(n : ℝ) / ab.2⌋₊ : ℝ) ^ p))).sum ≤ Cpl * (n : ℝ) ^ (p - 1) := hpl n hn₂ have hD_sum_nonneg : 0 ≤ (branches.map (fun ab => (ab.1 : ℝ) * (((n : ℝ) / ab.2) ^ p - (⌊(n : ℝ) / ab.2⌋₊ : ℝ) ^ p))).sum := by apply List.sum_nonneg intro x hx rw [List.mem_map] at hx rcases hx with ⟨ab, hab, rfl⟩ have hvalid_ab : BranchValid ab := hvalid ab hab have hb_pos : 0 < ab.2 := lt_trans (by norm_num : (0 : ℝ) < 1) hvalid_ab.2 have hfl : (⌊(n : ℝ) / ab.2⌋₊ : ℝ) ≤ (n : ℝ) / ab.2 := Nat.floor_le (div_nonneg (Nat.cast_nonneg n) (le_of_lt hb_pos)) have hpow_nonneg : 0 ≤ ((n : ℝ) / ab.2) ^ p - (⌊(n : ℝ) / ab.2⌋₊ : ℝ) ^ p := sub_nonneg.mpr (Real.rpow_le_rpow (by positivity) hfl hp.le) exact mul_nonneg (Nat.cast_nonneg ab.1) hpow_nonneg have hCint_nonneg : 0 ≤ Cint * (n : ℝ) := mul_nonneg (le_of_lt hCintpos) (Nat.cast_nonneg n) have hnp : (n : ℝ) * (n : ℝ) ^ (p - 1) = (n : ℝ) ^ p := by have hn_pos_real : 0 < (n : ℝ) := by exact_mod_cast (lt_of_lt_of_le (by norm_num : (0 : ℕ) < 1) hn_pos) calc (n : ℝ) * (n : ℝ) ^ (p - 1) = (n : ℝ) ^ (p - 1) * (n : ℝ) := by ring _ = (n : ℝ) ^ ((p - 1) + 1) := (Real.rpow_add_one (ne_of_gt hn_pos_real) (p - 1)).symm _ = (n : ℝ) ^ p := by rw [show (p - 1) + 1 = p by ring] have hsum_le : (branches.map (fun ab => (ab.1 : ℝ) * (((n : ℝ) / ab.2) ^ p - (⌊(n : ℝ) / ab.2⌋₊ : ℝ) ^ p) * (1 + akraBazziIntegral p g (⌊(n : ℝ) / ab.2⌋₊)))).sum ≤ (branches.map (fun ab => (ab.1 : ℝ) * (((n : ℝ) / ab.2) ^ p - (⌊(n : ℝ) / ab.2⌋₊ : ℝ) ^ p) * (1 + akraBazziIntegral p g n))).sum := by apply List.sum_le_sum intro ab hab have hvalid_ab : BranchValid ab := hvalid ab hab have hb_pos : 0 < ab.2 := lt_trans (by norm_num : (0 : ℝ) < 1) hvalid_ab.2 have hfl : (⌊(n : ℝ) / ab.2⌋₊ : ℝ) ≤ (n : ℝ) / ab.2 := Nat.floor_le (div_nonneg (Nat.cast_nonneg n) (le_of_lt hb_pos)) have hfloor_le_nat : ⌊(n : ℝ) / ab.2⌋₊ ≤ n := le_of_lt (floor_div_lt_self hvalid_ab.2 (Nat.succ_le_iff.mp hn_pos)) have hI_mono : akraBazziIntegral p g (⌊(n : ℝ) / ab.2⌋₊) ≤ akraBazziIntegral p g n := akraBazziIntegral_mono hgnonneg hfloor_le_nat have hpow_nonneg : 0 ≤ ((n : ℝ) / ab.2) ^ p - (⌊(n : ℝ) / ab.2⌋₊ : ℝ) ^ p := sub_nonneg.mpr (Real.rpow_le_rpow (by positivity) hfl hp.le) have hnonneg_a : 0 ≤ (ab.1 : ℝ) := Nat.cast_nonneg ab.1 exact mul_le_mul_of_nonneg_left (add_le_add_right hI_mono 1) (mul_nonneg hnonneg_a hpow_nonneg) calc (branches.map (fun ab => (ab.1 : ℝ) * (((n : ℝ) / ab.2) ^ p - (⌊(n : ℝ) / ab.2⌋₊ : ℝ) ^ p) * (1 + akraBazziIntegral p g (⌊(n : ℝ) / ab.2⌋₊)))).sum ≤ (branches.map (fun ab => (ab.1 : ℝ) * (((n : ℝ) / ab.2) ^ p - (⌊(n : ℝ) / ab.2⌋₊ : ℝ) ^ p) * (1 + akraBazziIntegral p g n))).sum := hsum_le _ = (1 + akraBazziIntegral p g n) * (branches.map (fun ab => (ab.1 : ℝ) * (((n : ℝ) / ab.2) ^ p - (⌊(n : ℝ) / ab.2⌋₊ : ℝ) ^ p))).sum := by rw [List.sum_map_mul_right branches (fun ab => (ab.1 : ℝ) * (((n : ℝ) / ab.2) ^ p - (⌊(n : ℝ) / ab.2⌋₊ : ℝ) ^ p)) (1 + akraBazziIntegral p g n)] ring _ ≤ (Cint * (n : ℝ)) * (Cpl * (n : ℝ) ^ (p - 1)) := by exact mul_le_mul hI_bound hpln hD_sum_nonneg hCint_nonneg _ = Cpl * Cint * (n : ℝ) * (n : ℝ) ^ (p - 1) := by ring _ = Cpl * Cint * (n : ℝ) ^ p := by rw [show Cpl * Cint * (n : ℝ) * (n : ℝ) ^ (p - 1) = Cpl * Cint * ((n : ℝ) * (n : ℝ) ^ (p - 1)) by ring] rw [hnp] _ ≤ M * g n := by have hg_lower : c₀ * (n : ℝ) ^ p ≤ g n := hglower n hn_pos have hfac : 0 ≤ Cpl * Cint / c₀ := le_of_lt (by positivity) have h2 := mul_le_mul_of_nonneg_left hg_lower hfac have h3 : (Cpl * Cint / c₀) * (c₀ * (n : ℝ) ^ p) = Cpl * Cint * (n : ℝ) ^ p := by field_simp [ne_of_gt hc₀pos] dsimp [M] rwa [h3] at h2 have hT_nonneg : ∀ n, 0 ≤ T n := akraBazzi_T_nonneg hvalid hgnonneg hsat let Smax : ℝ := ∑ m ∈ Finset.range (N₀ + 1), akraBazziScale p g m have hSmax_pos : 0 < Smax := by dsimp [Smax] have h1mem : 1 ∈ Finset.range (N₀ + 1) := by rw [Finset.mem_range]; omega have h1_scale : 0 < akraBazziScale p g 1 := by unfold akraBazziScale have h1p : 0 < (1 : ℝ) ^ p := Real.rpow_pos_of_pos (by norm_num : 0 < (1 : ℝ)) p have h1I : 0 ≤ akraBazziIntegral p g 1 := akraBazziIntegral_nonneg hgnonneg 1 positivity exact Finset.sum_pos' (by intro m _hm; exact akraBazziScale_nonneg hp.le hgnonneg m) ⟨1, h1mem, h1_scale⟩ let Tmin : ℝ := min 1 c₀ have hTmin_pos : 0 < Tmin := by dsimp [Tmin]; exact lt_min (by norm_num : (0 : ℝ) < 1) hc₀pos let c : ℝ := Tmin / (2 * (K + M + 1) * (Smax + 1)) have hc_pos : 0 < c := by dsimp [c]; positivity have hc_le_inv : c * (K + M) ≤ 1 := by dsimp [c, Tmin] have hden_pos : 0 < 2 * (K + M + 1) * (Smax + 1) := by positivity rw [div_mul_eq_mul_div, div_le_iff₀ hden_pos] have hTmin_le_one : min 1 c₀ ≤ 1 := min_le_left 1 c₀ have hTmin_nonneg : 0 ≤ min 1 c₀ := le_of_lt hTmin_pos have hSmax : 0 ≤ Smax := le_of_lt hSmax_pos have hKM : 0 ≤ K + M := le_of_lt (add_pos hKpos hMpos) nlinarith have hmain : ∀ n, c * akraBazziScale p g n ≤ T n := by intro n induction n using Nat.strong_induction_on with | h n ih => by_cases hn0 : n = 0 · subst n rw [hsat.1] have hF0 : akraBazziScale p g 0 = 0 := by unfold akraBazziScale akraBazziIntegral have h0pow : (0 : ℝ) ^ p = 0 := Real.zero_rpow (ne_of_gt hp) simp [h0pow] rw [hF0] simp · have hn_pos : 0 < n := Nat.pos_of_ne_zero hn0 have hn_pos' : 1 ≤ n := Nat.succ_le_iff.mpr hn_pos by_cases hle : n ≤ N₀ · have hscale_le_Smax : akraBazziScale p g n ≤ Smax := by dsimp [Smax] have hn_mem : n ∈ Finset.range (N₀ + 1) := by rw [Finset.mem_range] exact Nat.lt_succ_of_le hle exact Finset.single_le_sum (by intro m _hm; exact akraBazziScale_nonneg hp.le hgnonneg m) hn_mem have hT_ge_Tmin : Tmin ≤ T n := by dsimp [Tmin] by_cases hle₀ : n ≤ n₀ · rw [hsat.2.1 n hn_pos' hle₀] exact min_le_left 1 c₀ · have hn₀n : n₀ < n := lt_of_not_ge hle₀ have hg : c₀ * (n : ℝ) ^ p ≤ g n := hglower n hn_pos' have hT_ge_g : g n ≤ T n := akraBazzi_T_ge_g hvalid hgnonneg hsat n hn₀n have hnp : 1 ≤ (n : ℝ) ^ p := by have hn1 : 1 ≤ (n : ℝ) := by exact_mod_cast hn_pos' exact Real.one_le_rpow hn1 hp.le calc min 1 c₀ ≤ c₀ := min_le_right 1 c₀ _ ≤ c₀ * (n : ℝ) ^ p := by nlinarith [hc₀pos, hnp] _ ≤ g n := hg _ ≤ T n := hT_ge_g calc c * akraBazziScale p g n ≤ c * Smax := mul_le_mul_of_nonneg_left hscale_le_Smax hc_pos.le _ ≤ Tmin := by dsimp [c] have hden_pos : 0 < 2 * (K + M + 1) * (Smax + 1) := by positivity rw [div_mul_eq_mul_div, div_le_iff₀ hden_pos] have hSmax_nonneg : 0 ≤ Smax := le_of_lt hSmax_pos have hKM : 0 ≤ K + M := le_of_lt (add_pos hKpos hMpos) have hD_ge_Smax : Smax ≤ 2 * (K + M + 1) * (Smax + 1) := by nlinarith nlinarith [mul_le_mul_of_nonneg_left hD_ge_Smax (le_of_lt hTmin_pos)] _ ≤ T n := hT_ge_Tmin · have hN₀n : N₀ < n := lt_of_not_ge hle have hIH : ∀ ab ∈ branches, c * akraBazziScale p g (⌊(n : ℝ) / ab.2⌋₊) ≤ T (⌊(n : ℝ) / ab.2⌋₊) := by intro ab hab have hvalid_ab : BranchValid ab := hvalid ab hab have hlt : ⌊(n : ℝ) / ab.2⌋₊ < n := floor_div_lt_self hvalid_ab.2 hn_pos exact ih (⌊(n : ℝ) / ab.2⌋₊) hlt have hchildren : c * (branches.map (fun ab => (ab.1 : ℝ) * akraBazziScale p g (⌊(n : ℝ) / ab.2⌋₊))).sum ≤ (branches.map (fun ab => (ab.1 : ℝ) * T (⌊(n : ℝ) / ab.2⌋₊))).sum := by calc c * (branches.map (fun ab => (ab.1 : ℝ) * akraBazziScale p g (⌊(n : ℝ) / ab.2⌋₊))).sum = (branches.map (fun ab => (ab.1 : ℝ) * (c * akraBazziScale p g (⌊(n : ℝ) / ab.2⌋₊)))).sum := by rw [show (branches.map (fun ab => (ab.1 : ℝ) * (c * akraBazziScale p g (⌊(n : ℝ) / ab.2⌋₊)))).sum = (branches.map (fun ab => c * ((ab.1 : ℝ) * akraBazziScale p g (⌊(n : ℝ) / ab.2⌋₊)))).sum by exact congrArg List.sum (List.map_congr_left (by intro ab _hab; ring))] rw [List.sum_map_mul_left branches (fun ab => (ab.1 : ℝ) * akraBazziScale p g (⌊(n : ℝ) / ab.2⌋₊)) c] _ ≤ (branches.map (fun ab => (ab.1 : ℝ) * T (⌊(n : ℝ) / ab.2⌋₊))).sum := by apply List.sum_le_sum intro ab hab have hvalid_ab : BranchValid ab := hvalid ab hab exact mul_le_mul_of_nonneg_left (hIH ab hab) (Nat.cast_nonneg ab.1) have hN₀n' : N₀ ≤ n := le_of_lt hN₀n have hE := hEloss n hN₀n' have hincn : (branches.map (fun ab => akraBazziIncrement p g ab n)).sum ≤ K * g n := by have hn₁ : n₁ ≤ n := le_trans (Nat.le_max_left n₁ n₂) (by omega) exact hinc n hn₁ have hdecomp := akraBazzi_scale_floor_decomp branches p g n hvalid hroot calc c * akraBazziScale p g n = c * ((branches.map (fun ab => (ab.1 : ℝ) * akraBazziScale p g (⌊(n : ℝ) / ab.2⌋₊))).sum + (branches.map (fun ab => akraBazziIncrement p g ab n)).sum + (branches.map (fun ab => (ab.1 : ℝ) * (((n : ℝ) / ab.2) ^ p - (⌊(n : ℝ) / ab.2⌋₊ : ℝ) ^ p) * (1 + akraBazziIntegral p g (⌊(n : ℝ) / ab.2⌋₊)))).sum) := by rw [hdecomp] ring _ = c * (branches.map (fun ab => (ab.1 : ℝ) * akraBazziScale p g (⌊(n : ℝ) / ab.2⌋₊))).sum + c * (branches.map (fun ab => akraBazziIncrement p g ab n)).sum + c * (branches.map (fun ab => (ab.1 : ℝ) * (((n : ℝ) / ab.2) ^ p - (⌊(n : ℝ) / ab.2⌋₊ : ℝ) ^ p) * (1 + akraBazziIntegral p g (⌊(n : ℝ) / ab.2⌋₊)))).sum := by ring _ ≤ (branches.map (fun ab => (ab.1 : ℝ) * T (⌊(n : ℝ) / ab.2⌋₊))).sum + c * (K * g n) + c * (M * g n) := by have h1 := add_le_add_right hchildren (c * (branches.map (fun ab => akraBazziIncrement p g ab n)).sum + c * (branches.map (fun ab => (ab.1 : ℝ) * (((n : ℝ) / ab.2) ^ p - (⌊(n : ℝ) / ab.2⌋₊ : ℝ) ^ p) * (1 + akraBazziIntegral p g (⌊(n : ℝ) / ab.2⌋₊)))).sum) have h2 : c * (branches.map (fun ab => akraBazziIncrement p g ab n)).sum ≤ c * (K * g n) := mul_le_mul_of_nonneg_left hincn hc_pos.le have h3 : c * (branches.map (fun ab => (ab.1 : ℝ) * (((n : ℝ) / ab.2) ^ p - (⌊(n : ℝ) / ab.2⌋₊ : ℝ) ^ p) * (1 + akraBazziIntegral p g (⌊(n : ℝ) / ab.2⌋₊)))).sum ≤ c * (M * g n) := mul_le_mul_of_nonneg_left hE hc_pos.le linarith _ = (branches.map (fun ab => (ab.1 : ℝ) * T (⌊(n : ℝ) / ab.2⌋₊))).sum + c * (K + M) * g n := by ring _ ≤ (branches.map (fun ab => (ab.1 : ℝ) * T (⌊(n : ℝ) / ab.2⌋₊))).sum + g n := by have hg_nonneg : 0 ≤ g n := hgnonneg n have hcm : c * (K + M) ≤ 1 := hc_le_inv nlinarith _ = T n := by have hn₀_le_N₀ : n₀ ≤ N₀ := by omega have hn₀n : n₀ < n := lt_of_le_of_lt hn₀_le_N₀ hN₀n rw [hsat.2.2 n hn₀n] rw [Chapter03.isBigOmega_iff] refine ⟨c, hc_pos, 0, ?_⟩ intro n _hn rw [abs_of_nonneg (akraBazziScale_nonneg hp.le hgnonneg n)] rw [abs_of_nonneg (hT_nonneg n)] exact hmain n

Akra–Bazzi asymptotic bound (critical regime). When the forcing exponent equals the root exponent p, the solution satisfies T(n) = Θ(n^p (1 + Σ_{u≤n} g(u)/u^(p+1))).

theorem akraBazzi_bigTheta_critical {branches : List (ℕ × ℝ)} {g T : ℕ → ℝ} {n₀ : ℕ} {p : ℝ} (hvalid : BranchesValid branches) (hnonempty : branches ≠ []) (hroot : IsAkraBazziRoot branches p) (hp : 0 < p) (hsmooth : PolynomialGrowth g p) (hsat : SatisfiesAkraBazzi branches g T n₀) : Chapter03.isBigTheta T (akraBazziScale p g) := by constructor · exact akraBazzi_upper_bound hvalid hnonempty hroot hp hp.le hsmooth hsat · exact akraBazzi_lower_bound_critical hvalid hnonempty hroot hp hsmooth hsat

Akra–Bazzi asymptotic bound (forcing-dominated regime). For p + 1 ≤ q the solution satisfies T(n) = Θ(n^p (1 + Σ_{u≤n} g(u)/u^(p+1))).

theorem akraBazzi_bigTheta {branches : List (ℕ × ℝ)} {g T : ℕ → ℝ} {n₀ : ℕ} {p q : ℝ} (hvalid : BranchesValid branches) (hnonempty : branches ≠ []) (hroot : IsAkraBazziRoot branches p) (hp : 0 < p) (hpq : p + 1 ≤ q) (hsmooth : PolynomialGrowth g q) (hsat : SatisfiesAkraBazzi branches g T n₀) : Chapter03.isBigTheta T (akraBazziScale p g) := by constructor · exact akraBazzi_upper_bound hvalid hnonempty hroot hp (by linarith [hpq]) hsmooth hsat · exact akraBazzi_lower_bound hvalid hnonempty hroot hp hpq hsmooth hsat

The deep leaf-dominated regime (q < p)

The smoothing function ε x = 1 / √x, used to absorb the constant floor perturbation in the deep leaf-dominated regime q < p. It is nonnegative and strictly decreasing on the positive reals, and its drop ε(x/b) - ε x = (√b - 1) · x^(-1/2) dominates the one-step relative power loss of the floor.

noncomputable def akraBazziSmoothingFn (x : ℝ) : ℝ := (Real.sqrt x)⁻¹

Smoothing gain dominates the one-step floor loss. For b > 1 and p > 0, eventually (n/(2b))^p (√b - 1) / √n ≥ 2(1+p) (n/b)^(p-1) (1 + 1/√n), the comparison that lets the smoothing factor absorb the constant floor loss.

try 'simp' instead of 'simpa' Note: This linter can be disabled with `set_option linter.unnecessarySimpa false`Try this: [apply] ring_nf The `ring` tactic failed to close the goal. Use `ring_nf` to obtain a normal form. Note that `ring` works primarily in *commutative* rings. If you have a noncommutative ring, abelian group or module, consider using `noncomm_ring`, `abel` or `module` instead. lemma akraBazzi_smoothing_gain_ge_loss {b p : ℝ} (hb : 1 < b) (hp : 0 < p) : ∃ K : ℝ, 0 < K ∧ ∀ n : ℕ, 1 ≤ n → K ≤ (n : ℝ) ^ (1 / 2 : ℝ) → (n : ℝ) ^ p / (2 * b) ^ p * ((Real.sqrt b - 1) * (Real.sqrt (n : ℝ))⁻¹) ≥ (2 * (1 + p)) * ((n : ℝ) / b) ^ (p - 1) * (1 + (Real.sqrt (n : ℝ))⁻¹) := by have hb_pos : 0 < b := lt_trans (by norm_num : (0 : ℝ) < 1) hb have hb_sqrt_minus_one : 0 < Real.sqrt b - 1 := by have h1 : 1 < Real.sqrt b := by simpa using (Real.sqrt_lt_sqrt (by norm_num : 0 ≤ (1 : ℝ)) hb) linarith let C : ℝ := 2 * (1 + p) have hC_pos : 0 < C := by dsimp [C]; positivity let K : ℝ := 2 * C * (2 : ℝ) ^ p * b / (Real.sqrt b - 1) have hK_pos : 0 < K := by dsimp [K]; positivity refine ⟨K, hK_pos, ?_⟩ intro n hn1 hnK have hn_pos : 0 < (n : ℝ) := by exact_mod_cast (lt_of_lt_of_le (by norm_num : (0 : ℕ) < 1) hn1) have hsqrt_n_pos : 0 < Real.sqrt (n : ℝ) := (Real.sqrt_pos).2 hn_pos have hsqrt_eq : (n : ℝ) ^ (1 / 2 : ℝ) = Real.sqrt (n : ℝ) := (Real.sqrt_eq_rpow (n : ℝ)).symm have hkey : 2 * C * (2 : ℝ) ^ p * b ≤ (Real.sqrt b - 1) * Real.sqrt (n : ℝ) := by rw [hsqrt_eq] at hnK have h := (div_le_iff₀ hb_sqrt_minus_one).mp (by simpa [K] using hnK) nlinarith have hsqrt_ge_one : 1 ≤ Real.sqrt (n : ℝ) := by rw [← Real.sqrt_one] exact Real.sqrt_le_sqrt (by exact_mod_cast hn1) have hfinal : (n : ℝ) * (Real.sqrt b - 1) ≥ C * (2 : ℝ) ^ p * b * (Real.sqrt (n : ℝ) + 1) := by calc (n : ℝ) * (Real.sqrt b - 1) = (Real.sqrt (n : ℝ)) ^ 2 * (Real.sqrt b - 1) := by rw [Real.sq_sqrt (le_of_lt hn_pos)] _ = (Real.sqrt b - 1) * Real.sqrt (n : ℝ) * Real.sqrt (n : ℝ) := by ring _ ≥ (2 * C * (2 : ℝ) ^ p * b) * Real.sqrt (n : ℝ) := by exact mul_le_mul_of_nonneg_right hkey (le_of_lt hsqrt_n_pos) _ = 2 * C * (2 : ℝ) ^ p * b * Real.sqrt (n : ℝ) := by ring _ ≥ C * (2 : ℝ) ^ p * b * (Real.sqrt (n : ℝ) + 1) := by have h2sqrt : Real.sqrt (n : ℝ) + 1 ≤ 2 * Real.sqrt (n : ℝ) := by linarith have h := mul_le_mul_of_nonneg_left h2sqrt (by positivity : 0 ≤ C * (2 : ℝ) ^ p * b) nlinarith have hnp : (n : ℝ) ^ p = (n : ℝ) ^ (p - 1) * (n : ℝ) := by simpa [show p - 1 + 1 = p by ring] using (Real.rpow_add_one (ne_of_gt hn_pos) (p - 1)) have hfinal_np : (n : ℝ) ^ p * (Real.sqrt b - 1) ≥ C * (2 : ℝ) ^ p * b * (n : ℝ) ^ (p - 1) * (Real.sqrt (n : ℝ) + 1) := by have h := mul_le_mul_of_nonneg_right hfinal (Real.rpow_nonneg (le_of_lt hn_pos) (p - 1)) rw [show (n : ℝ) * (Real.sqrt b - 1) * (n : ℝ) ^ (p - 1) = (n : ℝ) ^ p * (Real.sqrt b - 1) by rw [hnp]; ring] at h nlinarith have h2bp : (2 * b) ^ p = (2 : ℝ) ^ p * b ^ p := by rw [Real.mul_rpow (by norm_num : 0 ≤ (2 : ℝ)) (le_of_lt hb_pos)] have hb_p : b ^ p = b * b ^ (p - 1) := by rw [show p = (p - 1) + 1 by ring] rw [Real.rpow_add_one (ne_of_gt hb_pos) (p - 1)] Try this: [apply] ring_nf The `ring` tactic failed to close the goal. Use `ring_nf` to obtain a normal form. Note that `ring` works primarily in *commutative* rings. If you have a noncommutative ring, abelian group or module, consider using `noncomm_ring`, `abel` or `module` instead.ring have hden_pos : 0 < (2 * b) ^ p * Real.sqrt (n : ℝ) := mul_pos (Real.rpow_pos_of_pos (by positivity : 0 < (2 : ℝ) * b) p) hsqrt_n_pos calc (n : ℝ) ^ p / (2 * b) ^ p * ((Real.sqrt b - 1) * (Real.sqrt (n : ℝ))⁻¹) = (n : ℝ) ^ p * (Real.sqrt b - 1) / ((2 * b) ^ p * Real.sqrt (n : ℝ)) := by ring _ ≥ (C * (2 : ℝ) ^ p * b * (n : ℝ) ^ (p - 1) * (Real.sqrt (n : ℝ) + 1)) / ((2 * b) ^ p * Real.sqrt (n : ℝ)) := by exact mul_le_mul_of_nonneg_right hfinal_np (inv_nonneg.mpr (le_of_lt hden_pos)) _ = C * ((n : ℝ) / b) ^ (p - 1) * (1 + (Real.sqrt (n : ℝ))⁻¹) := by rw [h2bp, hb_p] rw [Real.div_rpow (le_of_lt hn_pos) (le_of_lt hb_pos) (p - 1)] field_simp [hsqrt_n_pos.ne', hb_pos.ne', (Real.rpow_pos_of_pos (by norm_num : 0 < (2 : ℝ)) p).ne', (Real.rpow_pos_of_pos hb_pos (p - 1)).ne'] _ = (2 * (1 + p)) * ((n : ℝ) / b) ^ (p - 1) * (1 + (Real.sqrt (n : ℝ))⁻¹) := by try 'simp' instead of 'simpa' Note: This linter can be disabled with `set_option linter.unnecessarySimpa false`simpa [C]

Floored smoothing scale is a subsolution. For b > 1 and p > 0, eventually ⌊n/b⌋^p (1 + ε⌊n/b⌋) ≥ (n/b)^p (1 + ε n). This is the discrete analogue of the Kuszmaul–Leiserson smoothing step used by mathlib's AkraBazziRecurrence.­isTheta_asympBound: the factor 1 + ε absorbs the constant floor loss so that the homogeneous induction closes to Ω(n^p) exactly (rather than only Ω(n^(p-ε))).

lemma akraBazzi_smoothing_scale_floor_ge {b : ℝ} (hb : 1 < b) {p : ℝ} (hp : 0 < p) : ∃ N : ℕ, ∀ n : ℕ, N ≤ n → ((⌊(n : ℝ) / b⌋₊ : ℝ) ^ p) * (1 + akraBazziSmoothingFn (⌊(n : ℝ) / b⌋₊ : ℝ)) ≥ ((n : ℝ) / b) ^ p * (1 + akraBazziSmoothingFn (n : ℝ)) := by have hb_pos : 0 < b := lt_trans (by norm_num : (0 : ℝ) < 1) hb have hb_sqrt_minus_one : 0 < Real.sqrt b - 1 := by have h1 : 1 < Real.sqrt b := by simpa using (Real.sqrt_lt_sqrt (by norm_num : 0 ≤ (1 : ℝ)) hb) linarith let C : ℝ := 2 * (1 + p) have hC_pos : 0 < C := by dsimp [C]; positivity rcases akraBazzi_smoothing_gain_ge_loss hb hp with ⟨K, hK_pos, hK⟩ let N₁ : ℕ := Nat.ceil (K ^ (2 : ℕ)) + 1 have hN₁_gt : K ^ (2 : ℕ) < (N₁ : ℝ) := by have hceil : K ^ (2 : ℕ) ≤ (Nat.ceil (K ^ (2 : ℕ)) : ℝ) := Nat.le_ceil (K ^ (2 : ℕ)) exact lt_of_le_of_lt hceil (by exact_mod_cast (Nat.lt_succ_self (Nat.ceil (K ^ (2 : ℕ))))) let N₂ : ℕ := Nat.ceil (2 * b) + 1 have hN₂_gt : 2 * b < (N₂ : ℝ) := by have hceil : 2 * b ≤ (Nat.ceil (2 * b) : ℝ) := Nat.le_ceil (2 * b) exact lt_of_le_of_lt hceil (by exact_mod_cast (Nat.lt_succ_self (Nat.ceil (2 * b)))) refine ⟨max N₁ N₂, ?_⟩ intro n hn have hnN₁ : N₁ ≤ n := le_trans (Nat.le_max_left _ _) hn have hnN₂ : N₂ ≤ n := le_trans (Nat.le_max_right _ _) hn have hn1_nat : 1 ≤ n := le_trans (by omega : 1 ≤ N₁) hnN₁ have hn1 : 1 ≤ (n : ℝ) := by exact_mod_cast hn1_nat have hn_pos : 0 < (n : ℝ) := by linarith have hKsq_le : K ^ (2 : ℕ) ≤ (n : ℝ) := le_trans (le_of_lt hN₁_gt) (by exact_mod_cast hnN₁) have hnK : K ≤ (n : ℝ) ^ (1 / 2 : ℝ) := by have hsqrt : (n : ℝ) ^ (1 / 2 : ℝ) = Real.sqrt (n : ℝ) := (Real.sqrt_eq_rpow (n : ℝ)).symm rw [hsqrt] exact (Real.le_sqrt (le_of_lt hK_pos) (Nat.cast_nonneg n)).2 hKsq_le have hn2 : 2 * b ≤ (n : ℝ) := le_trans (le_of_lt hN₂_gt) (by exact_mod_cast hnN₂) let x : ℝ := (⌊(n : ℝ) / b⌋₊ : ℝ) have hnb_nonneg : 0 ≤ (n : ℝ) / b := div_nonneg (le_of_lt hn_pos) (le_of_lt hb_pos) have hx_le : x ≤ (n : ℝ) / b := by simpa [x] using (Nat.floor_le hnb_nonneg) have hx_ge : (n : ℝ) / b - 1 ≤ x := by have h := Nat.lt_floor_add_one ((n : ℝ) / b) simpa [x] using (le_of_lt (by linarith [h]) : (n : ℝ) / b - 1 ≤ x) have hx2 : 2 ≤ (n : ℝ) / b := by rw [le_div_iff₀ hb_pos] exact hn2 have hx_pos : 0 < x := by have h1 : 1 ≤ (n : ℝ) / b - 1 := by linarith [hx2] exact lt_of_lt_of_le (by norm_num : (0 : ℝ) < 1) (le_trans h1 hx_ge) have hx_ge_half : (n : ℝ) / (2 * b) ≤ x := by have hhalf_le : (n : ℝ) / (2 * b) ≤ (n : ℝ) / b - 1 := by have hdiv : (n : ℝ) / b - (n : ℝ) / (2 * b) = (n : ℝ) / (2 * b) := by field_simp [ne_of_gt (mul_pos (by norm_num : 0 < (2 : ℝ)) hb_pos)] ring have hnb_ge_one : 1 ≤ (n : ℝ) / (2 * b) := by rw [one_le_div (mul_pos (by norm_num : 0 < (2 : ℝ)) hb_pos)] exact hn2 linarith [hdiv, hnb_ge_one] exact le_trans hhalf_le hx_ge have hgain : (Real.sqrt b - 1) * akraBazziSmoothingFn (n : ℝ) ≤ akraBazziSmoothingFn x - akraBazziSmoothingFn (n : ℝ) := by unfold akraBazziSmoothingFn have hsqrt_nb_pos : 0 < Real.sqrt ((n : ℝ) / b) := (Real.sqrt_pos).2 (div_pos hn_pos hb_pos) have hsqrt_nb_inv : (Real.sqrt ((n : ℝ) / b))⁻¹ = Real.sqrt b / Real.sqrt (n : ℝ) := by rw [Real.sqrt_div (le_of_lt hn_pos) b, inv_div] have hsqrt_x_pos : 0 < Real.sqrt x := (Real.sqrt_pos).2 hx_pos have hsqrt_le : Real.sqrt x ≤ Real.sqrt ((n : ℝ) / b) := Real.sqrt_le_sqrt hx_le have hle : (Real.sqrt ((n : ℝ) / b))⁻¹ ≤ (Real.sqrt x)⁻¹ := (inv_le_inv₀ hsqrt_nb_pos hsqrt_x_pos).2 hsqrt_le rw [hsqrt_nb_inv] at hle have hle' : (Real.sqrt b) * (Real.sqrt (n : ℝ))⁻¹ ≤ (Real.sqrt x)⁻¹ := by simpa [div_eq_mul_inv] using hle calc (Real.sqrt b - 1) * (Real.sqrt (n : ℝ))⁻¹ = (Real.sqrt b) * (Real.sqrt (n : ℝ))⁻¹ - (Real.sqrt (n : ℝ))⁻¹ := by ring _ ≤ (Real.sqrt x)⁻¹ - (Real.sqrt (n : ℝ))⁻¹ := sub_le_sub_right hle' _ have hfloor_loss : ((n : ℝ) / b) ^ p - x ^ p ≤ C * ((n : ℝ) / b) ^ (p - 1) := by simpa [x, C] using (rpow_sub_floor_le_of_two_le hp (by simpa using hx2)) have hεn_nonneg : 0 ≤ akraBazziSmoothingFn (n : ℝ) := by unfold akraBazziSmoothingFn exact inv_nonneg.mpr (Real.sqrt_nonneg _) have hgain_ge_loss : ((n : ℝ) / b) ^ p - x ^ p + (((n : ℝ) / b) ^ p - x ^ p) * akraBazziSmoothingFn (n : ℝ) ≤ x ^ p * (akraBazziSmoothingFn x - akraBazziSmoothingFn (n : ℝ)) := by have hgain_lower_raw := hK n hn1_nat hnK have hgain_lower' : (n : ℝ) ^ p / (2 * b) ^ p * ((Real.sqrt b - 1) * akraBazziSmoothingFn (n : ℝ)) ≥ C * ((n : ℝ) / b) ^ (p - 1) * (1 + akraBazziSmoothingFn (n : ℝ)) := by simpa [akraBazziSmoothingFn, C] using hgain_lower_raw have hx_p_ge : (n : ℝ) ^ p / (2 * b) ^ p ≤ x ^ p := by have hhalf_nonneg : 0 ≤ (n : ℝ) / (2 * b) := div_nonneg (le_of_lt hn_pos) (le_of_lt (mul_pos (by norm_num : 0 < (2 : ℝ)) hb_pos)) have hpow : ((n : ℝ) / (2 * b)) ^ p ≤ x ^ p := Real.rpow_le_rpow hhalf_nonneg hx_ge_half hp.le rwa [Real.div_rpow (le_of_lt hn_pos) (le_of_lt (mul_pos (by norm_num : 0 < (2 : ℝ)) hb_pos)) p] at hpow have hgain_nonneg : 0 ≤ (Real.sqrt b - 1) * akraBazziSmoothingFn (n : ℝ) := mul_nonneg (le_of_lt hb_sqrt_minus_one) hεn_nonneg have hgain_lower : x ^ p * ((Real.sqrt b - 1) * akraBazziSmoothingFn (n : ℝ)) ≥ C * ((n : ℝ) / b) ^ (p - 1) * (1 + akraBazziSmoothingFn (n : ℝ)) := by exact le_trans hgain_lower' (mul_le_mul_of_nonneg_right hx_p_ge hgain_nonneg) have hgain_mul : x ^ p * (akraBazziSmoothingFn x - akraBazziSmoothingFn (n : ℝ)) ≥ C * ((n : ℝ) / b) ^ (p - 1) * (1 + akraBazziSmoothingFn (n : ℝ)) := by exact le_trans hgain_lower (mul_le_mul_of_nonneg_left hgain (Real.rpow_nonneg (le_of_lt hx_pos) p)) have hloss_le : ((n : ℝ) / b) ^ p - x ^ p + (((n : ℝ) / b) ^ p - x ^ p) * akraBazziSmoothingFn (n : ℝ) ≤ C * ((n : ℝ) / b) ^ (p - 1) * (1 + akraBazziSmoothingFn (n : ℝ)) := by have h1 : ((n : ℝ) / b) ^ p - x ^ p ≤ C * ((n : ℝ) / b) ^ (p - 1) := hfloor_loss have h2 : (((n : ℝ) / b) ^ p - x ^ p) * akraBazziSmoothingFn (n : ℝ) ≤ (C * ((n : ℝ) / b) ^ (p - 1)) * akraBazziSmoothingFn (n : ℝ) := mul_le_mul_of_nonneg_right h1 hεn_nonneg nlinarith exact le_trans hloss_le hgain_mul calc ((n : ℝ) / b) ^ p * (1 + akraBazziSmoothingFn (n : ℝ)) = x ^ p + x ^ p * akraBazziSmoothingFn (n : ℝ) + (((n : ℝ) / b) ^ p - x ^ p) * (1 + akraBazziSmoothingFn (n : ℝ)) := by ring _ ≤ x ^ p + x ^ p * akraBazziSmoothingFn (n : ℝ) + x ^ p * (akraBazziSmoothingFn x - akraBazziSmoothingFn (n : ℝ)) := by nlinarith [hgain_ge_loss] _ = x ^ p * (1 + akraBazziSmoothingFn x) := by ring

Floored smoothing scale is a subsolution (multi-branch). The single-branch estimate akraBazzi_smoothing_scale_floor_ge holds uniformly across a finite list of branches.

lemma akraBazzi_smoothing_scale_floor_ge_multi {branches : List (ℕ × ℝ)} {p : ℝ} (hvalid : BranchesValid branches) (hp : 0 < p) : ∃ N : ℕ, ∀ n : ℕ, N ≤ n → ∀ ab ∈ branches, ((⌊(n : ℝ) / ab.2⌋₊ : ℝ) ^ p) * (1 + akraBazziSmoothingFn (⌊(n : ℝ) / ab.2⌋₊ : ℝ)) ≥ ((n : ℝ) / ab.2) ^ p * (1 + akraBazziSmoothingFn (n : ℝ)) := by classical induction branches with | nil => exact ⟨0, by intro n _hn ab hab; simp at hab⟩ | cons hd tl ih => rcases ih (fun ab hab => hvalid ab (by simp [hab])) with ⟨Ntl, hNtl⟩ rcases akraBazzi_smoothing_scale_floor_ge (hvalid hd (by simp)).2 hp with ⟨Nhd, hNhd⟩ refine ⟨max Nhd Ntl, ?_⟩ intro n hn ab hab rw [List.mem_cons] at hab rcases hab with rfl | habtl · exact hNhd n (le_trans (Nat.le_max_left _ _) hn) · exact hNtl n (le_trans (Nat.le_max_right _ _) hn) ab habtl

Akra–Bazzi lower bound (leaf-dominated regime). When 0 ≤ q < p the solution is T(n) = Ω(n^p (1 + I n)). The leaf term n^p dominates the driving term g = Θ(n^q), and the integral I n is bounded (akraBazziIntegral_bounded_of_lt), so the scale n^p(1 + I n) is Θ(n^p). The induction is closed by the smoothing factor akraBazziSmoothingFn via akraBazzi_smoothing_scale_floor_ge.

theorem akraBazzi_lower_bound_leaf {branches : List (ℕ × ℝ)} {g T : ℕ → ℝ} {n₀ : ℕ} {p q : ℝ} (hvalid : BranchesValid branches) (Variable name `hnonempty` is not explicitly referenced. The binding can be removed (if unused) or named `_` (if used implicitly). Note: This linter can be disabled with `set_option linter.unusedVariables false`hnonempty : branches ≠ []) (hroot : IsAkraBazziRoot branches p) (hp : 0 < p) (hpq : q < p) (hq : 0 ≤ q) (hsmooth : PolynomialGrowth g q) (hsat : SatisfiesAkraBazzi branches g T n₀) : Chapter03.isBigOmega T (akraBazziScale p g) := by have hgnonneg : ∀ n, 0 ≤ g n := hsmooth.1 rcases hsmooth.2.2 with ⟨c₀, _C₀, hc₀pos, _hC₀pos, hglower, _hgupper⟩ have hT_nonneg : ∀ n, 0 ≤ T n := akraBazzi_T_nonneg hvalid hgnonneg hsat have hT_ge_g : ∀ n, n₀ < n → g n ≤ T n := akraBazzi_T_ge_g hvalid hgnonneg hsat rcases akraBazziIntegral_bounded_of_lt hsmooth hpq with ⟨Ci, hIbound⟩ have hIbound0 : 0 ≤ Ci := by simpa [akraBazziIntegral] using (hIbound 0) rcases akraBazzi_smoothing_scale_floor_ge_multi hvalid hp with ⟨N, hN⟩ let N₀ : ℕ := max n₀ N + 1 let F : ℕ → ℝ := fun n => (n : ℝ) ^ p * (1 + akraBazziSmoothingFn (n : ℝ)) let Tmin : ℝ := min 1 c₀ have hTmin_pos : 0 < Tmin := lt_min (by norm_num : (0 : ℝ) < 1) hc₀pos let Smax : ℝ := ∑ m ∈ Finset.range (N₀ + 1), F m have hF_nonneg : ∀ m : ℕ, 0 ≤ F m := by intro m have hmp : 0 ≤ (m : ℝ) ^ p := Real.rpow_nonneg (by positivity) p have hε : 0 ≤ akraBazziSmoothingFn (m : ℝ) := by unfold akraBazziSmoothingFn; exact inv_nonneg.mpr (Real.sqrt_nonneg _) exact mul_nonneg hmp (add_nonneg (by norm_num : 0 ≤ (1 : ℝ)) hε) have hSmax_pos : 0 < Smax := by dsimp [Smax] have h1mem : 1 ∈ Finset.range (N₀ + 1) := by rw [Finset.mem_range]; omega have hF1 : 0 < F 1 := by dsimp [F] have h1p : 0 < ((1 : ℕ) : ℝ) ^ p := Real.rpow_pos_of_pos (by norm_num : 0 < ((1 : ℕ) : ℝ)) p have hε1 : 0 ≤ akraBazziSmoothingFn ((1 : ℕ) : ℝ) := by unfold akraBazziSmoothingFn; exact inv_nonneg.mpr (Real.sqrt_nonneg _) have h1plus : 0 < 1 + akraBazziSmoothingFn ((1 : ℕ) : ℝ) := by linarith exact mul_pos h1p h1plus exact Finset.sum_pos' (by intro m _hm; exact hF_nonneg m) ⟨1, h1mem, hF1⟩ let c : ℝ := Tmin / (2 * (Smax + 1)) have hc_pos : 0 < c := by dsimp [c]; positivity have hc_Smax_le_Tmin : c * Smax ≤ Tmin := by dsimp [c] have hden_pos : 0 < 2 * (Smax + 1) := by positivity rw [div_mul_eq_mul_div, div_le_iff₀ hden_pos] have hSmax_le_den : Smax ≤ 2 * (Smax + 1) := by nlinarith nlinarith [mul_le_mul_of_nonneg_left hSmax_le_den (le_of_lt hTmin_pos)] have hmain : ∀ n : ℕ, c * F n ≤ T n := by intro n induction n using Nat.strong_induction_on with | h n ih => by_cases hn0 : n = 0 · subst n rw [hsat.1] dsimp [F] have h0pow : (0 : ℝ) ^ p = 0 := Real.zero_rpow (ne_of_gt hp) simp [h0pow] · have hn_pos : 0 < n := Nat.pos_of_ne_zero hn0 have hn_pos' : 1 ≤ n := Nat.succ_le_iff.mpr hn_pos by_cases hle : n ≤ N₀ · have hF_le_Smax : F n ≤ Smax := by dsimp [Smax] have hn_mem : n ∈ Finset.range (N₀ + 1) := by rw [Finset.mem_range]; exact Nat.lt_succ_of_le hle exact Finset.single_le_sum (by intro m _hm; exact hF_nonneg m) hn_mem have hT_ge_Tmin : Tmin ≤ T n := by dsimp [Tmin] by_cases hle₀ : n ≤ n₀ · rw [hsat.2.1 n hn_pos' hle₀]; exact min_le_left 1 c₀ · have hn₀n : n₀ < n := lt_of_not_ge hle₀ have hg : c₀ * (n : ℝ) ^ q ≤ g n := hglower n hn_pos' have hT_ge_g_n : g n ≤ T n := hT_ge_g n hn₀n have hnq : 1 ≤ (n : ℝ) ^ q := by have hn1 : 1 ≤ (n : ℝ) := by exact_mod_cast hn_pos' exact Real.one_le_rpow hn1 hq calc min 1 c₀ ≤ c₀ := min_le_right 1 c₀ _ ≤ c₀ * (n : ℝ) ^ q := by nlinarith [hc₀pos, hnq] _ ≤ g n := hg _ ≤ T n := hT_ge_g_n calc c * F n ≤ c * Smax := mul_le_mul_of_nonneg_left hF_le_Smax hc_pos.le _ ≤ Tmin := hc_Smax_le_Tmin _ ≤ T n := hT_ge_Tmin · have hN₀n : N₀ < n := lt_of_not_ge hle have hNn : N ≤ n := by have hN₀_ge_N : N ≤ N₀ := by dsimp [N₀] exact Nat.le_trans (Nat.le_max_right n₀ N) (Nat.le_succ (max n₀ N)) exact le_trans hN₀_ge_N (le_of_lt hN₀n) have hIH : ∀ ab ∈ branches, c * F (⌊(n : ℝ) / ab.2⌋₊) ≤ T (⌊(n : ℝ) / ab.2⌋₊) := by intro ab hab have hvalid_ab : BranchValid ab := hvalid ab hab have hlt : ⌊(n : ℝ) / ab.2⌋₊ < n := floor_div_lt_self hvalid_ab.2 hn_pos exact ih (⌊(n : ℝ) / ab.2⌋₊) hlt have hchildren : c * (branches.map (fun ab => (ab.1 : ℝ) * F (⌊(n : ℝ) / ab.2⌋₊))).sum ≤ (branches.map (fun ab => (ab.1 : ℝ) * T (⌊(n : ℝ) / ab.2⌋₊))).sum := by calc c * (branches.map (fun ab => (ab.1 : ℝ) * F (⌊(n : ℝ) / ab.2⌋₊))).sum = (branches.map (fun ab => c * ((ab.1 : ℝ) * F (⌊(n : ℝ) / ab.2⌋₊)))).sum := by rw [List.sum_map_mul_left] _ = (branches.map (fun ab => (ab.1 : ℝ) * (c * F (⌊(n : ℝ) / ab.2⌋₊)))).sum := by apply congrArg List.sum apply List.map_congr_left intro ab _hab; ring _ ≤ (branches.map (fun ab => (ab.1 : ℝ) * T (⌊(n : ℝ) / ab.2⌋₊))).sum := by apply List.sum_le_sum intro ab hab exact mul_le_mul_of_nonneg_left (hIH ab hab) (Nat.cast_nonneg ab.1) have hsmoothing : ∀ ab ∈ branches, ((⌊(n : ℝ) / ab.2⌋₊ : ℝ) ^ p) * (1 + akraBazziSmoothingFn (⌊(n : ℝ) / ab.2⌋₊ : ℝ)) ≥ ((n : ℝ) / ab.2) ^ p * (1 + akraBazziSmoothingFn (n : ℝ)) := hN n hNn have hchildren' : c * (branches.map (fun ab => (ab.1 : ℝ) * (((n : ℝ) / ab.2) ^ p * (1 + akraBazziSmoothingFn (n : ℝ))))).sum ≤ c * (branches.map (fun ab => (ab.1 : ℝ) * F (⌊(n : ℝ) / ab.2⌋₊))).sum := by apply mul_le_mul_of_nonneg_left _ hc_pos.le apply List.sum_le_sum intro ab hab have h : ((n : ℝ) / ab.2) ^ p * (1 + akraBazziSmoothingFn (n : ℝ)) ≤ F (⌊(n : ℝ) / ab.2⌋₊) := by simpa [F] using (hsmoothing ab hab) exact mul_le_mul_of_nonneg_left h (Nat.cast_nonneg ab.1) have hscale_smooth : (branches.map (fun ab => (ab.1 : ℝ) * (((n : ℝ) / ab.2) ^ p * (1 + akraBazziSmoothingFn (n : ℝ))))).sum = F n := by have hscale : (branches.map (fun ab => (ab.1 : ℝ) * ((n : ℝ) / ab.2) ^ p)).sum = (n : ℝ) ^ p := akraBazzi_root_scale_invariance branches p hvalid hroot n dsimp [F] calc (branches.map (fun ab => (ab.1 : ℝ) * (((n : ℝ) / ab.2) ^ p * (1 + akraBazziSmoothingFn (n : ℝ))))).sum = (branches.map (fun ab => ((ab.1 : ℝ) * ((n : ℝ) / ab.2) ^ p) * (1 + akraBazziSmoothingFn (n : ℝ)))).sum := by apply congrArg List.sum; apply List.map_congr_left; intro ab _hab; ring _ = (branches.map (fun ab => (ab.1 : ℝ) * ((n : ℝ) / ab.2) ^ p)).sum * (1 + akraBazziSmoothingFn (n : ℝ)) := by rw [List.sum_map_mul_right] _ = (n : ℝ) ^ p * (1 + akraBazziSmoothingFn (n : ℝ)) := by rw [hscale] have hhn₀n : n₀ < n := by have hN₀_ge_n₀ : n₀ ≤ N₀ := by dsimp [N₀] exact Nat.le_trans (Nat.le_max_left n₀ N) (Nat.le_succ (max n₀ N)) exact lt_of_le_of_lt hN₀_ge_n₀ hN₀n calc c * F n = c * (branches.map (fun ab => (ab.1 : ℝ) * (((n : ℝ) / ab.2) ^ p * (1 + akraBazziSmoothingFn (n : ℝ))))).sum := by rw [hscale_smooth] _ ≤ c * (branches.map (fun ab => (ab.1 : ℝ) * F (⌊(n : ℝ) / ab.2⌋₊))).sum := hchildren' _ ≤ (branches.map (fun ab => (ab.1 : ℝ) * T (⌊(n : ℝ) / ab.2⌋₊))).sum := hchildren _ ≤ (branches.map (fun ab => (ab.1 : ℝ) * T (⌊(n : ℝ) / ab.2⌋₊))).sum + g n := le_add_of_nonneg_right (hgnonneg n) _ = T n := by rw [hsat.2.2 n hhn₀n] rw [Chapter03.isBigOmega_iff] refine ⟨c / (1 + Ci), ?_, 0, ?_⟩ · positivity · intro n hn rw [abs_of_nonneg (akraBazziScale_nonneg hp.le hgnonneg n)] rw [abs_of_nonneg (hT_nonneg n)] have hscale_le : akraBazziScale p g n ≤ (1 + Ci) * (n : ℝ) ^ p := by unfold akraBazziScale have hI : akraBazziIntegral p g n ≤ Ci := hIbound n have h1 : 1 + akraBazziIntegral p g n ≤ 1 + Ci := by linarith have hnp : 0 ≤ (n : ℝ) ^ p := Real.rpow_nonneg (by positivity) p calc (n : ℝ) ^ p * (1 + akraBazziIntegral p g n) ≤ (n : ℝ) ^ p * (1 + Ci) := mul_le_mul_of_nonneg_left h1 hnp _ = (1 + Ci) * (n : ℝ) ^ p := by ring have hεn_nonneg : 0 ≤ akraBazziSmoothingFn (n : ℝ) := by unfold akraBazziSmoothingFn; exact inv_nonneg.mpr (Real.sqrt_nonneg _) have hnp_le : (n : ℝ) ^ p ≤ F n := by dsimp [F] have hnp_nonneg : 0 ≤ (n : ℝ) ^ p := Real.rpow_nonneg (by positivity) p nlinarith [mul_nonneg hnp_nonneg hεn_nonneg] calc c / (1 + Ci) * akraBazziScale p g n ≤ c / (1 + Ci) * ((1 + Ci) * (n : ℝ) ^ p) := mul_le_mul_of_nonneg_left hscale_le (le_of_lt (div_pos hc_pos (by positivity : 0 < 1 + Ci))) _ = c * (n : ℝ) ^ p := by field_simp [ne_of_gt (by positivity : 0 < 1 + Ci)] _ ≤ c * F n := mul_le_mul_of_nonneg_left hnp_le hc_pos.le _ ≤ T n := hmain n

Akra–Bazzi asymptotic bound (leaf-dominated regime). For 0 ≤ q < p the solution satisfies T(n) = Θ(n^p (1 + Σ_{u≤n} g(u)/u^(p+1))): the upper bound akraBazzi_upper_bound and the leaf lower bound akraBazzi_lower_bound_leaf both apply.

theorem akraBazzi_bigTheta_leaf {branches : List (ℕ × ℝ)} {g T : ℕ → ℝ} {n₀ : ℕ} {p q : ℝ} (hvalid : BranchesValid branches) (hnonempty : branches ≠ []) (hroot : IsAkraBazziRoot branches p) (hp : 0 < p) (hpq : q < p) (hq : 0 ≤ q) (hsmooth : PolynomialGrowth g q) (hsat : SatisfiesAkraBazzi branches g T n₀) : Chapter03.isBigTheta T (akraBazziScale p g) := by constructor · exact akraBazzi_upper_bound hvalid hnonempty hroot hp hq hsmooth hsat · exact akraBazzi_lower_bound_leaf hvalid hnonempty hroot hp hpq hq hsmooth hsat
end Chapter04end CLRS

Definitions and proofs

CLRSLean.FourthEdition.Chapter_04.Section_04_7_Akra_Bazzi.Generalized

Akra–Bazzi comparison for all nonnegative polynomial forcing exponents

The discrete power-sum estimate closes the forcing interval p < q < p + 1. At characteristic root zero, induction on the integral itself handles the zero input without requiring a positive power. The resulting theorem akraBazzi_bigTheta_nonneg covers every p, q ≥ 0 under PolynomialGrowth and the floor recurrence SatisfiesAkraBazzi.

This retains the explicit monomial sandwich and monotonicity assumptions on forcing. It does not assert the unrestricted perturbation theorem.

open scoped BigOperatorsnamespace CLRS.Chapter04

The positive power sum estimate, including decreasing summands.

lemma akraBazzi_sum_rpow_le {d : ℝ} (hd : 0 < d) (hd1 : d ≤ 1) (n : ℕ) : (∑ u ∈ Finset.range n, ((u + 1 : ℕ) : ℝ) ^ (d - 1)) ≤ (1 + 1 / d) * (n : ℝ) ^ d := by cases n with | zero => simp [Real.zero_rpow hd.ne'] | succ k => have hant : AntitoneOn (fun x : ℝ => x ^ (d - 1)) (Set.Icc 1 (1 + (k : ℝ))) := by intro x hx y hy hxy exact Real.rpow_le_rpow_of_nonpos (by linarith [hx.1]) hxy (by linarith) have hsum := hant.sum_le_integral have hint : (∫ x : ℝ in 1..1 + (k : ℝ), x ^ (d - 1)) = (((k + 1 : ℕ) : ℝ) ^ d - 1) / d := by rw [integral_rpow (Or.inl (by linarith : -1 < d - 1))] simp only [sub_add_cancel, Real.one_rpow, Nat.cast_add, Nat.cast_one] rw [add_comm (1 : ℝ)] rw [hint] at hsum have hshift : (∑ u ∈ Finset.range (k + 1), ((u + 1 : ℕ) : ℝ) ^ (d - 1)) = 1 + ∑ u ∈ Finset.range k, (1 + ((u + 1 : ℕ) : ℝ)) ^ (d - 1) := by rw [Finset.sum_range_succ'] simp only [Nat.cast_add, Nat.cast_one, zero_add, Real.one_rpow] rw [add_comm] congr 1 apply Finset.sum_congr rfl intro u hu congr 1; ring rw [hshift] have hnp : 1 ≤ ((k + 1 : ℕ) : ℝ) ^ d := Real.one_le_rpow (by exact_mod_cast Nat.succ_le_succ (Nat.zero_le k)) hd.le have hdiv : 0 ≤ d⁻¹ := inv_nonneg.mpr hd.le calc 1 + ∑ u ∈ Finset.range k, (1 + ((u + 1 : ℕ) : ℝ)) ^ (d - 1) ≤ 1 + ((((k + 1 : ℕ) : ℝ) ^ d - 1) / d) := add_le_add_right hsum 1 _ ≤ (1 + 1 / d) * ((k + 1 : ℕ) : ℝ) ^ d := by rw [sub_div, div_eq_mul_inv, one_div] nlinarith

The discrete integral has polynomial growth whenever the forcing exponent strictly exceeds the characteristic exponent.

lemma akraBazzi_integral_le_poly_of_lt {p q : ℝ} {g : ℕ → ℝ} (hsmooth : PolynomialGrowth g q) (hpq : p < q) : ∃ C : ℝ, 0 < C ∧ ∀ n, akraBazziIntegral p g n ≤ C * (n : ℝ) ^ (q - p) := by by_cases hlarge : p + 1 ≤ q · exact akraBazzi_integral_le_poly hsmooth hlarge rcases hsmooth.2.2 with ⟨c, Cg, hc, hCg, hlower, hupper⟩ have hd : 0 < q - p := sub_pos.mpr hpq refine ⟨Cg * (1 + 1 / (q - p)), by positivity, ?_⟩ intro n calc akraBazziIntegral p g n ≤ ∑ u ∈ Finset.range n, Cg * ((u + 1 : ℕ) : ℝ) ^ (q - p - 1) := by apply Finset.sum_le_sum intro u hu have hu0 : 0 < ((u + 1 : ℕ) : ℝ) := by positivity calc g (u + 1) / ((u + 1 : ℕ) : ℝ) ^ (p + 1) ≤ (Cg * ((u + 1 : ℕ) : ℝ) ^ q) / ((u + 1 : ℕ) : ℝ) ^ (p + 1) := div_le_div_of_nonneg_right (hupper _ (by omega)) (Real.rpow_nonneg hu0.le _) _ = Cg * ((u + 1 : ℕ) : ℝ) ^ (q - p - 1) := by rw [mul_div_assoc, ← Real.rpow_sub hu0] congr 2; ring _ = Cg * (∑ u ∈ Finset.range n, ((u + 1 : ℕ) : ℝ) ^ (q - p - 1)) := (Finset.mul_sum ..).symm _ ≤ Cg * ((1 + 1 / (q - p)) * (n : ℝ) ^ (q - p)) := mul_le_mul_of_nonneg_left (akraBazzi_sum_rpow_le hd (by linarith) n) hCg.le _ = _ := by ring

Every strictly larger forcing exponent dominates the integral scale.

theorem akraBazzi_lower_bound_of_lt {branches : List (ℕ × ℝ)} {g T : ℕ → ℝ} {n₀ : ℕ} {p q : ℝ} (hvalid : BranchesValid branches) (_hnonempty : branches ≠ []) (_hroot : IsAkraBazziRoot branches p) (hp : 0 ≤ p) (hpq : p < q) (hsmooth : PolynomialGrowth g q) (hsat : SatisfiesAkraBazzi branches g T n₀) : Chapter03.isBigOmega T (akraBazziScale p g) := by have hgnonneg : ∀ n, 0 ≤ g n := hsmooth.1 rcases hsmooth.2.2 with ⟨c₀, _C, hc₀pos, _hCpos, hglower, _hgupper⟩ rcases akraBazzi_integral_le_poly_of_lt hsmooth hpq with ⟨Ci, _hCipos, hCi⟩ have hT_nonneg : ∀ n, 0 ≤ T n := akraBazzi_T_nonneg hvalid hgnonneg hsat have hT_ge_g : ∀ n, n₀ < n → g n ≤ T n := akraBazzi_T_ge_g hvalid hgnonneg hsat let c : ℝ := c₀ / (1 + Ci) have hc_pos : 0 < c := div_pos hc₀pos (by positivity) rw [Chapter03.isBigOmega_iff] refine ⟨c, hc_pos, n₀ + 1, ?_⟩ intro n hn have hn₀n : n₀ < n := by omega have hn1 : 1 ≤ n := by omega have hg_lower : c₀ * (n : ℝ) ^ q ≤ g n := hglower n hn1 have hF_le : akraBazziScale p g n ≤ (1 + Ci) * (n : ℝ) ^ q := by unfold akraBazziScale have hI : akraBazziIntegral p g n ≤ Ci * (n : ℝ) ^ (q - p) := hCi n have hnp : (n : ℝ) ^ p ≤ (n : ℝ) ^ q := by have hn' : 1 ≤ (n : ℝ) := by exact_mod_cast hn1 exact Real.rpow_le_rpow_of_exponent_le hn' (by linarith [hpq] : p ≤ q) calc (n : ℝ) ^ p * (1 + akraBazziIntegral p g n) ≤ (n : ℝ) ^ p * (1 + Ci * (n : ℝ) ^ (q - p)) := mul_le_mul_of_nonneg_left (by linarith) (Real.rpow_nonneg (by positivity) p) _ = (n : ℝ) ^ p + Ci * ((n : ℝ) ^ p * (n : ℝ) ^ (q - p)) := by ring _ = (n : ℝ) ^ p + Ci * (n : ℝ) ^ q := by have hn_pos : 0 < (n : ℝ) := by exact_mod_cast (lt_of_lt_of_le (by norm_num : (0:ℕ)<1) hn1) rw [show (n : ℝ) ^ p * (n : ℝ) ^ (q - p) = (n : ℝ) ^ q by rw [← Real.rpow_add hn_pos p (q - p)] congr 1; ring] _ ≤ (n : ℝ) ^ q + Ci * (n : ℝ) ^ q := by nlinarith [hnp] _ = (1 + Ci) * (n : ℝ) ^ q := by ring rw [abs_of_nonneg (akraBazziScale_nonneg hp hgnonneg n)] rw [abs_of_nonneg (hT_nonneg n)] calc c * akraBazziScale p g n ≤ c * ((1 + Ci) * (n : ℝ) ^ q) := mul_le_mul_of_nonneg_left hF_le hc_pos.le _ = c₀ * (n : ℝ) ^ q := by dsimp [c] field_simp [ne_of_gt (by positivity : 0 < 1 + Ci)] _ ≤ g n := hg_lower _ ≤ T n := hT_ge_g n hn₀n

At root zero there is no power-rounding loss in the discrete integral.

lemma akraBazzi_integral_children_zero {branches : List (ℕ × ℝ)} {g : ℕ → ℝ} (hvalid : BranchesValid branches) (hroot : IsAkraBazziRoot branches 0) (n : ℕ) : (branches.map (fun ab => (ab.1 : ℝ) * akraBazziIntegral 0 g ⌊(n : ℝ) / ab.2⌋₊)).sum = akraBazziIntegral 0 g n - (branches.map (fun ab => akraBazziIncrement 0 g ab n)).sum := by have hcoeff : (branches.map (fun ab => (ab.1 : ℝ))).sum = 1 := by simpa [IsAkraBazziRoot, charFun, charTerm] using hroot have hd := akraBazzi_scale_decomp branches 0 g n hvalid hroot simp only [akraBazziScale, Real.rpow_zero, one_mul, mul_one, mul_add, List.sum_map_add] at hd rw [hcoeff] at hd linarith

A zero characteristic exponent also admits the lower integral comparison. The induction uses the integral alone because it vanishes at input zero.

theorem akraBazzi_lower_bound_zero {branches : List (ℕ × ℝ)} {g T : ℕ → ℝ} {n₀ : ℕ} {q : ℝ} (hvalid : BranchesValid branches) (hnonempty : branches ≠ []) (hroot : IsAkraBazziRoot branches 0) (hq : 0 ≤ q) (hsmooth : PolynomialGrowth g q) (hsat : SatisfiesAkraBazzi branches g T n₀) : Chapter03.isBigOmega T (akraBazziScale 0 g) := by have hgn := hsmooth.1 have hTn := akraBazzi_T_nonneg hvalid hgn hsat rcases hsmooth.2.2 with ⟨c₀, C₀, hc₀, hC₀, hgl, hgu⟩ rcases akraBazzi_increment_upper hvalid hnonempty (p := 0) (by norm_num) hgn hsmooth.2.1 with ⟨K, hK, N, hinc⟩ let M : ℕ := max n₀ N + 1 let A : ℝ := min 1 c₀ have hA : 0 < A := lt_min (by norm_num) hc₀ have hI : 0 ≤ akraBazziIntegral 0 g M := akraBazziIntegral_nonneg hgn M let c : ℝ := min (A / (akraBazziIntegral 0 g M + 1)) (1 / (K + 1)) have hc : 0 < c := lt_min (div_pos hA (by positivity)) (by positivity) have hcA : c * (akraBazziIntegral 0 g M + 1) ≤ A := by exact (le_div_iff₀ (by positivity)).mp (min_le_left _ _) have hcK : c * K ≤ 1 := by have h : c * (K + 1) ≤ 1 := (le_div_iff₀ (by positivity)).mp (min_le_right _ _) nlinarith have hTpos : ∀ n, 1 ≤ n → A ≤ T n := by intro n hn by_cases hb : n ≤ n₀ · rw [hsat.2.1 n hn hb] exact min_le_left _ _ · have hforce := akraBazzi_T_ge_g hvalid hgn hsat n (by omega) have hpow : 1 ≤ (n : ℝ) ^ q := Real.one_le_rpow (by exact_mod_cast hn) hq have hlo := hgl n hn have hAc : A ≤ c₀ := min_le_right _ _ nlinarith have hmain : ∀ n, c * akraBazziIntegral 0 g n ≤ T n := by intro n induction n using Nat.strong_induction_on with | h n ih => by_cases hz : n = 0 · subst n simp [akraBazziIntegral, hsat.1] have hn : 1 ≤ n := by omega by_cases hsmall : n ≤ M · have hmono := akraBazziIntegral_mono hgn hsmall (p := 0) have hle := mul_le_mul_of_nonneg_left hmono hc.le have hpos := hTpos n hn nlinarith · have hchild : c * (branches.map (fun ab => (ab.1 : ℝ) * akraBazziIntegral 0 g ⌊(n : ℝ) / ab.2⌋₊)).sum ≤ (branches.map (fun ab => (ab.1 : ℝ) * T ⌊(n : ℝ) / ab.2⌋₊)).sum := by rw [← List.sum_map_mul_left] apply List.sum_le_sum intro ab hab have hh := ih ⌊(n : ℝ) / ab.2⌋₊ (floor_div_lt_self (hvalid ab hab).2 (by omega)) calc c * ((ab.1 : ℝ) * akraBazziIntegral 0 g ⌊(n : ℝ) / ab.2⌋₊) = (ab.1 : ℝ) * (c * akraBazziIntegral 0 g ⌊(n : ℝ) / ab.2⌋₊) := by ring _ ≤ _ := mul_le_mul_of_nonneg_left hh (Nat.cast_nonneg _) rw [akraBazzi_integral_children_zero hvalid hroot n] at hchild have hi := hinc n (show N ≤ n by dsimp [M] at hsmall; omega) have hice := mul_le_mul_of_nonneg_left hi hc.le have hgce := mul_le_mul_of_nonneg_right hcK (hgn n) rw [hsat.2.2 n (by dsimp [M] at hsmall; omega)] nlinarith rcases akraBazziIntegral_lower_const (p := 0) hsmooth with ⟨d, hd, hdI⟩ rw [Chapter03.isBigOmega_iff] refine ⟨c * d / (d + 1), by positivity, 1, ?_⟩ intro n hn rw [abs_of_nonneg (akraBazziScale_nonneg (by norm_num) hgn n), abs_of_nonneg (hTn n)] have hIn := hdI n hn have hcompare : d / (d + 1) * (1 + akraBazziIntegral 0 g n) ≤ akraBazziIntegral 0 g n := by rw [div_mul_eq_mul_div, div_le_iff₀ (by positivity)] nlinarith have hm := mul_le_mul_of_nonneg_left hcompare hc.le simp only [akraBazziScale, Real.rpow_zero, one_mul] calc c * d / (d + 1) * (1 + akraBazziIntegral 0 g n) = c * (d / (d + 1) * (1 + akraBazziIntegral 0 g n)) := by ring _ ≤ c * akraBazziIntegral 0 g n := hm _ ≤ T n := hmain n

All nonnegative polynomial forcing exponents satisfy the discrete Akra–Bazzi comparison, including the zero characteristic root.

theorem akraBazzi_bigTheta_nonneg {branches : List (ℕ × ℝ)} {g T : ℕ → ℝ} {n₀ : ℕ} {p q : ℝ} (hvalid : BranchesValid branches) (hnonempty : branches ≠ []) (hroot : IsAkraBazziRoot branches p) (hp : 0 ≤ p) (hq : 0 ≤ q) (hsmooth : PolynomialGrowth g q) (hsat : SatisfiesAkraBazzi branches g T n₀) : Chapter03.isBigTheta T (akraBazziScale p g) := by refine ⟨akraBazzi_upper_bound_nonneg hvalid hnonempty hroot hp hq hsmooth hsat, ?_⟩ rcases eq_or_lt_of_le hp with hp0 | hp0 · subst p exact akraBazzi_lower_bound_zero hvalid hnonempty hroot hq hsmooth hsat rcases lt_trichotomy q p with hlt | heq | hgt · exact akraBazzi_lower_bound_leaf hvalid hnonempty hroot hp0 hlt hq hsmooth hsat · subst q exact akraBazzi_lower_bound_critical hvalid hnonempty hroot hp0 hsmooth hsat · exact akraBazzi_lower_bound_of_lt hvalid hnonempty hroot hp hgt hsmooth hsat
end CLRS.Chapter04

CLRSLean.Chapter_04.Section_04_6_Master_Theorem_All_Input

The real-log comparison scale n^(log_b a). This is the textbook scale used in the standard CLRS statement of the Master theorem: the homogeneous-solution growth rate without floors and ceilings.

For integer exponents it coincides with the ordinary polynomial scale polynomialScale.

noncomputable def realLogScale (a b : ℕ) (n : ℕ) : ℝ := (n : ℝ) ^ (realLogExponent a b)

CLRSLean.FourthEdition.Chapter_04.Section_04_6_Continuous_Master_Theorem

Continuous master theorem, case 1. When the ratio a / b^p > 1 (so p < log_b a, i.e. the forcing n^p is polynomially smaller than the critical n^(log_b a)), the recursion-tree work is Θ(a^k), matching Θ(n^(log_b a)) on the exact power n = b^k.

theorem continuous_master_case1 (a b p : ℕ) (hb : (b : ℝ) ≠ 0) (hr : 1 < continuousRatio a b p) : Chapter03.isBigTheta (continuousWork a b p) (fun k => (a : ℝ) ^ k) := by have hratio : 1 < (a : ℝ) / (b : ℝ) ^ p := by simpa [continuousRatio] using hr have hpow_identity : ∀ k, (b : ℝ) ^ (p * k) * ((a : ℝ) / (b : ℝ) ^ p) ^ k = (a : ℝ) ^ k := by intro k calc (b : ℝ) ^ (p * k) * ((a : ℝ) / (b : ℝ) ^ p) ^ k = (b : ℝ) ^ (p * k) * ((a : ℝ) ^ k / (b : ℝ) ^ (p * k)) := by rw [div_pow, ← pow_mul] _ = (a : ℝ) ^ k := by field_simp [pow_ne_zero (p * k) hb] constructor · rw [Chapter03.isBigO_iff] refine ⟨((a : ℝ) / (b : ℝ) ^ p - 1)⁻¹, inv_pos.mpr (sub_pos.mpr hratio), 0, ?_⟩ intro k hk rw [abs_of_nonneg (continuousWork_nonneg a b p k)] rw [abs_of_nonneg (pow_nonneg (Nat.cast_nonneg a) k)] rw [continuousWork_eq_geomSum a b p hb k] have hgeom : geomSum ((a : ℝ) / (b : ℝ) ^ p) k ≤ ((a : ℝ) / (b : ℝ) ^ p - 1)⁻¹ * ((a : ℝ) / (b : ℝ) ^ p) ^ k := geomSum_le_geometric_of_gt_one hratio k have hterm : (b : ℝ) ^ (p * k) * geomSum ((a : ℝ) / (b : ℝ) ^ p) k ≤ ((a : ℝ) / (b : ℝ) ^ p - 1)⁻¹ * (a : ℝ) ^ k := by calc (b : ℝ) ^ (p * k) * geomSum ((a : ℝ) / (b : ℝ) ^ p) k ≤ (b : ℝ) ^ (p * k) * (((a : ℝ) / (b : ℝ) ^ p - 1)⁻¹ * ((a : ℝ) / (b : ℝ) ^ p) ^ k) := mul_le_mul_of_nonneg_left hgeom (pow_nonneg (Nat.cast_nonneg b) (p * k)) _ = ((a : ℝ) / (b : ℝ) ^ p - 1)⁻¹ * ((b : ℝ) ^ (p * k) * ((a : ℝ) / (b : ℝ) ^ p) ^ k) := by ring _ = ((a : ℝ) / (b : ℝ) ^ p - 1)⁻¹ * (a : ℝ) ^ k := by rw [hpow_identity k] exact hterm · rw [Chapter03.isBigOmega_iff] refine ⟨((a : ℝ) / (b : ℝ) ^ p)⁻¹, inv_pos.mpr (lt_trans zero_lt_one hratio), 1, ?_⟩ intro k hk rw [abs_of_nonneg (continuousWork_nonneg a b p k)] rw [abs_of_nonneg (pow_nonneg (Nat.cast_nonneg a) k)] rw [continuousWork_eq_geomSum a b p hb k] have hgeom : ((a : ℝ) / (b : ℝ) ^ p)⁻¹ * ((a : ℝ) / (b : ℝ) ^ p) ^ k ≤ geomSum ((a : ℝ) / (b : ℝ) ^ p) k := geometric_le_geomSum_of_gt_one hratio hk have hterm : ((a : ℝ) / (b : ℝ) ^ p)⁻¹ * (a : ℝ) ^ k ≤ (b : ℝ) ^ (p * k) * geomSum ((a : ℝ) / (b : ℝ) ^ p) k := by calc ((a : ℝ) / (b : ℝ) ^ p)⁻¹ * (a : ℝ) ^ k = ((a : ℝ) / (b : ℝ) ^ p)⁻¹ * ((b : ℝ) ^ (p * k) * ((a : ℝ) / (b : ℝ) ^ p) ^ k) := by rw [hpow_identity k] _ = (b : ℝ) ^ (p * k) * (((a : ℝ) / (b : ℝ) ^ p)⁻¹ * ((a : ℝ) / (b : ℝ) ^ p) ^ k) := by ring _ ≤ (b : ℝ) ^ (p * k) * geomSum ((a : ℝ) / (b : ℝ) ^ p) k := mul_le_mul_of_nonneg_left hgeom (pow_nonneg (Nat.cast_nonneg b) (p * k)) exact hterm

Continuous master theorem, case 2. When the ratio a / b^p = 1 (so a = b^p, i.e. the forcing n^p matches the critical n^(log_b a)), the recursion-tree work is Θ(k · a^k), the logarithmic case.

theorem continuous_master_case2 (a b p : ℕ) (hb : (b : ℝ) ≠ 0) (hr : continuousRatio a b p = 1) : Chapter03.isBigTheta (continuousWork a b p) (fun k => (k : ℝ) * (a : ℝ) ^ k) := by have hratio : (a : ℝ) / (b : ℝ) ^ p = 1 := by simpa [continuousRatio] using hr have hbp : (b : ℝ) ^ p ≠ 0 := pow_ne_zero p hb have hbase : (b : ℝ) ^ p = (a : ℝ) := by field_simp [hbp] at hratio exact hratio.symm have hpow_identity : ∀ k, (b : ℝ) ^ (p * k) = (a : ℝ) ^ k := by intro k rw [← hbase] rw [← pow_mul] constructor · rw [Chapter03.isBigO_iff] refine ⟨1, by norm_num, 0, ?_⟩ intro k hk rw [abs_of_nonneg (continuousWork_nonneg a b p k)] rw [abs_of_nonneg (mul_nonneg (Nat.cast_nonneg k) (pow_nonneg (Nat.cast_nonneg a) k))] rw [continuousWork_eq_geomSum a b p hb k] rw [continuousRatio, hratio, geomSum_eq_of_one k] rw [hpow_identity k] nlinarith · rw [Chapter03.isBigOmega_iff] refine ⟨1, by norm_num, 0, ?_⟩ intro k hk rw [abs_of_nonneg (continuousWork_nonneg a b p k)] rw [abs_of_nonneg (mul_nonneg (Nat.cast_nonneg k) (pow_nonneg (Nat.cast_nonneg a) k))] rw [continuousWork_eq_geomSum a b p hb k] rw [continuousRatio, hratio, geomSum_eq_of_one k] rw [hpow_identity k] nlinarith

Continuous master theorem, case 3. When the ratio a / b^p < 1 (so p > log_b a, i.e. the forcing n^p dominates the critical n^(log_b a)), the recursion-tree work is Θ(b^(p·k)), matching Θ(f(n)) = Θ(n^p) on the exact power n = b^k.

theorem continuous_master_case3 (a b p : ℕ) (hb : (b : ℝ) ≠ 0) (hr0 : 0 ≤ continuousRatio a b p) (hr1 : continuousRatio a b p < 1) : Chapter03.isBigTheta (continuousWork a b p) (fun k => (b : ℝ) ^ (p * k)) := by have hratio0 : 0 ≤ (a : ℝ) / (b : ℝ) ^ p := by simpa [continuousRatio] using hr0 have hratio1 : (a : ℝ) / (b : ℝ) ^ p < 1 := by simpa [continuousRatio] using hr1 constructor · rw [Chapter03.isBigO_iff] refine ⟨(1 - (a : ℝ) / (b : ℝ) ^ p)⁻¹, inv_pos.mpr (sub_pos.mpr hratio1), 0, ?_⟩ intro k hk rw [abs_of_nonneg (continuousWork_nonneg a b p k)] rw [abs_of_nonneg (pow_nonneg (Nat.cast_nonneg b) (p * k))] rw [continuousWork_eq_geomSum a b p hb k] rw [continuousRatio] have hgeom : geomSum ((a : ℝ) / (b : ℝ) ^ p) k ≤ (1 - (a : ℝ) / (b : ℝ) ^ p)⁻¹ := geomSum_le_of_lt_one hratio0 hratio1 k have hle := mul_le_mul_of_nonneg_left hgeom (pow_nonneg (Nat.cast_nonneg b) (p * k)) simpa [mul_comm, mul_left_comm, mul_assoc] using hle · rw [Chapter03.isBigOmega_iff] refine ⟨1, by norm_num, 1, ?_⟩ intro k hk have hk1 : 1 ≤ k := hk rw [abs_of_nonneg (continuousWork_nonneg a b p k)] rw [abs_of_nonneg (pow_nonneg (Nat.cast_nonneg b) (p * k))] rw [continuousWork_eq_geomSum a b p hb k] rw [continuousRatio] have hgeom : 1 ≤ geomSum ((a : ℝ) / (b : ℝ) ^ p) k := by rw [geomSum] rw [show (1 : ℝ) = ((a : ℝ) / (b : ℝ) ^ p) ^ 0 by simp] exact Finset.single_le_sum (f := fun j => ((a : ℝ) / (b : ℝ) ^ p) ^ j) (by intro j hj; exact pow_nonneg hratio0 j) (a := 0) (by rw [mem_range]; exact lt_of_lt_of_le zero_lt_one hk1) simpa [mul_comm, mul_left_comm, mul_assoc] using (mul_le_mul_of_nonneg_left hgeom (pow_nonneg (Nat.cast_nonneg b) (p * k)))

Scope and implementation notes

Imports

Current source

Sections 4.1--4.7 are native fourth-edition sections:

  • Section 4.1 — Multiplying square matrices: the recursive eight-product SQUARE-MATRIX-MULTIPLY-RECURSIVE and its Θ(n³) scalar work, proved from the counted execution on side lengths 2^k.

  • Section 4.2 — Strassen's algorithm for matrix multiplication: the seven-product block algebra, the recursive power-of-two algorithm, and its Θ(n^(log₂ 7)) scalar work from the same counted execution.

  • Section 4.3 — The substitution method: one-step upper-bound, lower-bound, and sandwich substitution templates.

  • Section 4.4 — The recursion-tree method: exact additive level unrolling, explicit finite branching trees, internal level-plus-leaf decomposition, reusable geometric bounds, and the textbook 3T(n/4)+cn² and T(n/3)+T(2n/3)+cn calculations. Alongside the exact-real common-depth model, natural-size floor/ceiling trees now execute to their base cases, permit unequal child depths, and agree exactly with independently stated recurrence equations. Their actual total costs now have proved Θ(n²) and Θ(n log n) bounds for positive local-cost coefficients and nonnegative base costs.

  • Section 4.5 — The master method: normalized recurrence expansion and the three Master-style exact-power criteria (including the polylog case-2 extension), with case 3 derived from eventual forcing regularity rather than an assumed solution bound.

  • Section 4.6 — Proof of the continuous master theorem: the real geometric-series core and the three continuous cases, bridged to the discrete comparison scales.

  • Section 4.7 — Akra–Bazzi recurrences: the recurrence hypotheses, the root equation, the multi-branch root uniqueness and nonnegativity, the scale-invariance bridge, and the integral asymptotic form for every nonnegative root and forcing exponent under the explicit PolynomialGrowth monomial sandwich and floor recurrence.

Declarations retain the CLRS.Chapter04 namespace during the compatibility period; the third-edition-numbered imports CLRSLean.Chapter_04 and CLRSLean.Chapter_04.Section_04_* forward to these sources.

Coverage boundary

The proved boundary consists of the models and hypotheses stated above. Matrix execution uses depth-indexed power-of-two squares; padOne embeds one such square into the next depth and is not an arbitrary-dimension padding interface. Its counter charges scalar ring operations, excluding indexing, allocation, and the internal cost of each scalar operation. The continuous layer is a real-valued geometric calculation at natural depth. Akra–Bazzi retains monotone forcing between positive multiples of one monomial and explicit floor children; it does not assert unrestricted forcing or perturbations. Maximum subarray belongs to Online Material; the third-edition all-input Master detail remains in the legacy tree as its own source.

See docs/clrs-fourth-edition-map.csv for the section-level mapping and docs/migrations/clrs4.md for compatibility and deprecation policy.

CLRS, fourth edition · Chapter 4 of 35