Imports
CLRS Section 35.5 - Work bounds for costed APPROX-SUBSET-SUM
The bounds in this file concern the counters produced by the concrete
execution in Costed.Execution. The model charges one unit for each list
addition, comparison, or outer-loop composition step.
noncomputable sectionnamespace CLRSnamespace ApproxSubsetSum
Every intermediate list is bounded using the one trimming parameter
selected from the original input length n. In particular, the parameter is
not recomputed from the length of the remaining suffix.
theorem approxLists_uniform_length_bound {n t : Nat} {ε : Real}
(hn : 0 < n) (hε0 : 0 < ε) (hε1 : ε ≤ 1) (ht : 1 ≤ t) (ys : List Nat) :
((approxLists (ε / (2 * (n : Real))) t ys).length : Real) ≤
4 * (n : Real) * Real.log (t : Real) / ε + 2 := by
let δ : Real := ε / (2 * (n : Real))
have hnpos : 0 < (n : Real) := by exact_mod_cast hn
have hn1 : (1 : Real) ≤ (n : Real) := by exact_mod_cast hn
have hδ0 : 0 < δ := by
dsimp [δ]
exact div_pos hε0 (by positivity)
have hδle : δ ≤ 1 := by
dsimp [δ]
have h2n : (1 : Real) ≤ 2 * (n : Real) := by nlinarith [hn1]
have hεn : ε ≤ 2 * (n : Real) := le_trans hε1 h2n
exact (div_le_iff₀ (by positivity : (0 : Real) < 2 * (n : Real))).mpr
(by simpa using hεn)
have hlogpos : 0 < Real.log (1 + δ) := Real.log_pos (by linarith)
have hlogt : 0 ≤ Real.log (t : Real) :=
Real.log_nonneg (by exact_mod_cast ht)
have hlogd : δ / 2 ≤ Real.log (1 + δ) :=
log_one_add_ge_half (le_of_lt hδ0) hδle
have hscaled : Real.log (t : Real) / Real.log (1 + δ) ≤
4 * (n : Real) * Real.log (t : Real) / ε := by
have hδ2 : 0 < δ / 2 := by positivity
have hinv : (Real.log (1 + δ))⁻¹ ≤ (δ / 2)⁻¹ :=
(inv_le_inv₀ hlogpos hδ2).2 hlogd
have hδ2inv : (δ / 2)⁻¹ = 4 * (n : Real) / ε := by
have h2d : (2 : Real) / δ = 4 * (n : Real) / ε := by
dsimp [δ]
field_simp [hε0.ne', ne_of_gt hnpos]
ring
calc
(δ / 2)⁻¹ = 2 / δ := by field_simp [ne_of_gt hδ0]
_ = 4 * (n : Real) / ε := h2d
calc
Real.log (t : Real) / Real.log (1 + δ) =
Real.log (t : Real) * (Real.log (1 + δ))⁻¹ := by ring
_ ≤ Real.log (t : Real) * (δ / 2)⁻¹ :=
mul_le_mul_of_nonneg_left hinv hlogt
_ = Real.log (t : Real) * (4 * (n : Real) / ε) := by rw [hδ2inv]
_ = 4 * (n : Real) * Real.log (t : Real) / ε := by ring
have hlen := approxLists_length_bound (δ := δ) hδ0 t ht ys
have hfinal : Real.log (t : Real) / Real.log (1 + δ) + 2 ≤
4 * (n : Real) * Real.log (t : Real) / ε + 2 := by
linarith
have hgoal : ((approxLists δ t ys).length : Real) ≤
4 * (n : Real) * Real.log (t : Real) / ε + 2 :=
le_trans hlen hfinal
simpa [δ] using hgoalThe five local scans performed in one outer iteration use at most seven times the prior list length.
private theorem localPipeline_work_le (δ : Real) (t x : Nat) (L : List Nat) :
let shifted := mapAddWithCost x L
let merged := mergeWithCost L shifted.value
let trimmed := trimWithCost δ merged.value
let kept := filterAtMostWithCost t trimmed.value
shifted.work + merged.work + trimmed.work + kept.work ≤ 7 * L.length := by
dsimp only
have hshiftWork := mapAddWithCost_work x L
have hshiftLen : (mapAddWithCost x L).value.length = L.length := by
rw [mapAddWithCost_value]
simp
have hmergeWork := mergeWithCost_work_le L (mapAddWithCost x L).value
have hmergeLen := mergeWithCost_length L (mapAddWithCost x L).value
have htrimWork :=
trimWithCost_work_le δ (mergeWithCost L (mapAddWithCost x L).value).value
have htrimLen :=
trimWithCost_length_le δ (mergeWithCost L (mapAddWithCost x L).value).value
have hfilterWork := filterAtMostWithCost_work t
(trimWithCost δ (mergeWithCost L (mapAddWithCost x L).value).value).value
omega
If all semantic intermediate lists are bounded by B, the actual outer
execution counter is at most length * (7B + 1).
theorem approxListsWithCost_work_le_of_length_bound {δ B : Real} {t : Nat}
(_hB : 0 ≤ B)
(hbound : ∀ ys, ((approxLists δ t ys).length : Real) ≤ B) (xs : List Nat) :
((approxListsWithCost δ t xs).work : Real) ≤
(xs.length : Real) * (7 * B + 1) := by
induction xs with
| nil => simp [approxListsWithCost]
| cons x xs ih =>
let prior := approxListsWithCost δ t xs
let shifted := mapAddWithCost x prior.value
let merged := mergeWithCost prior.value shifted.value
let trimmed := trimWithCost δ merged.value
let kept := filterAtMostWithCost t trimmed.value
have hprior : (prior.work : Real) ≤ (xs.length : Real) * (7 * B + 1) := by
simpa [prior] using ih
have hlen : (prior.value.length : Real) ≤ B := by
rw [approxListsWithCost_value]
exact hbound xs
have hpipeline :
shifted.work + merged.work + trimmed.work + kept.work ≤
7 * prior.value.length := by
simpa [shifted, merged, trimmed, kept] using
localPipeline_work_le δ t x prior.value
have hpipelineReal :
((shifted.work + merged.work + trimmed.work + kept.work : Nat) : Real) ≤
7 * (prior.value.length : Real) := by
exact_mod_cast hpipeline
push_cast at hpipelineReal
have hstep :
((shifted.work + merged.work + trimmed.work + kept.work + 1 : Nat) : Real) ≤
7 * B + 1 := by
push_cast
nlinarith
rw [approxListsWithCost]
dsimp only
change ((prior.work + shifted.work + merged.work + trimmed.work + kept.work + 1 : Nat) : Real) ≤
(((xs.length + 1 : Nat) : Real) * (7 * B + 1))
push_cast
push_cast at hstep
ring_nf at hprior hstep ⊢
nlinarith
The counter returned by the complete execution is polynomial in the input
length, log t, and 1 / ε. The bound includes construction of every
intermediate list and the final maximum scan.
theorem approxSubsetSumWithCost_work_le {xs : List Nat} {t : Nat} {ε : Real}
(hε0 : 0 < ε) (hε1 : ε ≤ 1) (ht : 1 ≤ t) :
((approxSubsetSumWithCost xs t ε).work : Real) ≤
48 * ((xs.length : Real) + 1) ^ 2 *
(Real.log (t : Real) + 1) / ε := by
have hlog : 0 ≤ Real.log (t : Real) :=
Real.log_nonneg (by exact_mod_cast ht)
by_cases hnil : xs = []
· subst xs
have hratio : 1 ≤ (Real.log (t : Real) + 1) / ε := by
apply (le_div_iff₀ hε0).2
linarith
have h48 : (0 : Real) ≤ 48 := by norm_num
simp only [List.length_nil, Nat.cast_zero, zero_add, one_pow]
rw [approxSubsetSumWithCost]
simp [approxListsWithCost, maximumWithCost, maximumAuxWithCost]
calc
(1 : Real) ≤ 48 := by norm_num
_ ≤ 48 * ((Real.log (t : Real) + 1) / ε) :=
by simpa using mul_le_mul_of_nonneg_left hratio h48
_ = 48 * (Real.log (t : Real) + 1) / ε := by ring
· have hn : 0 < xs.length := by
exact Nat.pos_of_ne_zero (fun hlen => hnil (List.eq_nil_of_length_eq_zero hlen))
let n : Real := xs.length
let B : Real := 4 * n * Real.log (t : Real) / ε + 2
have hn0 : 0 ≤ n := by positivity
have hn1 : 1 ≤ n := by
dsimp [n]
exact_mod_cast hn
have hB0 : 0 ≤ B := by
dsimp [B]
have : 0 ≤ 4 * n * Real.log (t : Real) / ε := by positivity
linarith
have hbound : ∀ ys,
((approxLists (ε / (2 * (xs.length : Real))) t ys).length : Real) ≤ B := by
intro ys
simpa [B, n] using
(approxLists_uniform_length_bound (n := xs.length) hn hε0 hε1 ht ys)
have hlists := approxListsWithCost_work_le_of_length_bound
(δ := ε / (2 * (xs.length : Real))) (B := B) hB0 hbound xs
have hvalueLen :
((approxListsWithCost (ε / (2 * (xs.length : Real))) t xs).value.length : Real) ≤ B := by
rw [approxListsWithCost_value]
exact hbound xs
have htotal :
((approxSubsetSumWithCost xs t ε).work : Real) ≤
n * (7 * B + 1) + B := by
rw [approxSubsetSumWithCost]
simp only
rw [maximumWithCost_work]
push_cast
simpa [n] using add_le_add hlists hvalueLen
have hBstep : B ≤ 7 * B + 1 := by nlinarith
have hcoarse :
((approxSubsetSumWithCost xs t ε).work : Real) ≤
(n + 1) * (7 * B + 1) := by
calc
((approxSubsetSumWithCost xs t ε).work : Real)
≤ n * (7 * B + 1) + B := htotal
_ ≤ n * (7 * B + 1) + (7 * B + 1) := add_le_add_right hBstep _
_ = (n + 1) * (7 * B + 1) := by ring
have hnlog : n * Real.log (t : Real) ≤
(n + 1) * (Real.log (t : Real) + 1) := by
nlinarith [mul_nonneg hn0 hlog]
have hεprod : ε ≤ (n + 1) * (Real.log (t : Real) + 1) := by
have hone : 1 ≤ (n + 1) * (Real.log (t : Real) + 1) := by
nlinarith [mul_nonneg hn0 hlog]
exact le_trans hε1 hone
have hinside :
28 * n * Real.log (t : Real) + 15 * ε ≤
48 * (n + 1) * (Real.log (t : Real) + 1) := by
nlinarith
have hpoly : (n + 1) * (7 * B + 1) ≤
48 * (n + 1) ^ 2 * (Real.log (t : Real) + 1) / ε := by
have hleft : (n + 1) * (7 * B + 1) =
((n + 1) * (28 * n * Real.log (t : Real) + 15 * ε)) / ε := by
dsimp [B]
field_simp [hε0.ne']
ring
rw [hleft]
apply (div_le_div_iff_of_pos_right hε0).2
have hmul := mul_le_mul_of_nonneg_left hinside (by linarith : 0 ≤ n + 1)
nlinarith
simpa [n] using le_trans hcoarse hpolyKernel-checked FPTAS bundle for the costed execution: feasibility, approximation quality, and polynomial work all refer to the same run.
theorem approxSubsetSumWithCost_fptas {xs : List Nat} {t : Nat} {ε : Real}
(hε0 : 0 < ε) (hε1 : ε ≤ 1) (ht : 1 ≤ t) :
let run := approxSubsetSumWithCost xs t ε
run.value ∈ subsetSums xs ∧
run.value ≤ t ∧
(optimalSum xs t : Real) ≤ (1 + ε) * (run.value : Real) ∧
(run.work : Real) ≤
48 * ((xs.length : Real) + 1) ^ 2 *
(Real.log (t : Real) + 1) / ε := by
dsimp only
refine ⟨?_, ?_, ?_, approxSubsetSumWithCost_work_le hε0 hε1 ht⟩
· rw [approxSubsetSumWithCost_value]
exact approxSum_mem_subsetSums xs t ε
· rw [approxSubsetSumWithCost_value]
exact approxSum_le_t xs t ε
· rw [approxSubsetSumWithCost_value]
exact approxSubsetSum_approx_lt xs t ε hε0 hε1end ApproxSubsetSumend CLRS