Imports
import Mathlib11.4. Open Addressing
Open addressing stores every key directly in the table Fin m → Option K (no
chains). Each key k has a probe sequence ⟨h(k,0), h(k,1), …, h(k,m-1)⟩, a
permutation of the slots; a search or insertion walks the probe order until it
finds the key (success), an empty slot (stop), or exhausts the table.
This section formalises three layers.
Main results:
-
Functional model (CLRS §11.4 operational layer).
-
openInsert/openSearch: insert into the first empty slot along the probe order; search until the key or the first empty slot. -
Theorem
openSearch_eq_false_of_absent: a key that is nowhere in the table is not found (absent key not found). -
Theorem
openSearch_openInsert: after inserting a key along a duplicate-free probe order that has an empty slot, a search finds it (inserted key is found).
-
-
Probe schemes (CLRS §11.4, equations (11.5)-(11.7)).
-
linearProbe,quadraticProbe,doubleHashProbeoverZMod m. -
Theorem
linearProbe_bijective: linear probing enumerates every slot. -
Theorem
doubleHashProbe_bijective: double hashing enumerates every slot when the second hash is a unit (coprime tom, CLRS requirement). -
Theorem
quadraticProbe_zero: quadratic probing starts at the base slot.
-
-
Expected-probe bounds under uniform hashing (CLRS Theorems 11.6-11.8).
-
probeTail: the uniform-hashing probability that the firstiprobes of an unsuccessful search all hit occupied slots, the without-replacement product∏_{j<i} (n-j)/(m-j)(CLRS §11.4). -
Theorem
probeTail_le_pow: each such probability is at mostα^i, the per-factor bound(n-j)/(m-j) ≤ n/mthat CLRS uses. -
Theorem
expectedUnsuccessfulProbes_le: expected unsuccessful-search probes≤ 1/(1-α)(CLRS Theorem 11.6), as the tail-sum∑_i probeTail. -
Theorem
expectedInsertionProbes_le: the same1/(1-α)bound for an insertion (CLRS Corollary 11.7). -
Theorem
expectedSuccessfulProbes_le: expected successful-search probes≤ (1/α) * ∑_{j<n} 1/(m-j) = (1/α)(H_m - H_{m-n}), the harmonic form of CLRS Theorem 11.8. -
Theorem
expectedSuccessfulProbes_le_ln: expected successful-search probes≤ (1/α) * ln(1/(1-α)), the logarithmic (closed-form) version of CLRS Theorem 11.8.
-
Status: proved for the functional model, the probe schemes, and the
uniform-hashing expected-probe bounds.
Notation conventions used in this section:
-
m: the number of table slots -
n: the number of stored keys;α = n/mis the load factor (openLoadFactor) -
K: the key type; a slot isOption K(none= empty) -
probeTail m n i:P[first i probes all occupied], the uniform-hashing tail -
H_k: thek-th harmonic number∑_{r=1}^{k} 1/r
The companion UniformProbe development derives the without-replacement
tails from an explicit uniform permutation sample space and identifies their
tail sum with the actual first-empty-slot probe count. The upper bounds retain
their non-full-load hypotheses; the logarithmic successful-search bound requires
0 < n < m. Successful expectation averages insertion-time occupancies.
RAM and cache costs remain outside this model.
namespace CLRSnamespace Chapter11Functional open-addressing model
The table maps each of the m slots to Option K (none = empty). A probe
order is a List of slots (the slot type is left abstract; the probe schemes
below instantiate it at ZMod m). scanFind and scanInsertPos walk a
probe order once.
Walk a probe order looking for k, stopping at the first empty slot: return
true if a slot holding k is reached before any empty slot (open-addressing
search semantics).
def scanFind {S K : Type*} [DecidableEq K] (T : S → Option K) (k : K) :
List S → Bool
| [] => false
| s :: rest =>
if T s = some k then true
else if T s = none then false
else scanFind T k rest
Walk a probe order returning the first empty slot, or none if the order has
no empty slot (table full along this probe order).
def scanInsertPos {S K : Type*} [DecidableEq K] (T : S → Option K) :
List S → Option S
| [] => none
| s :: rest => if T s = none then some s else scanInsertPos T restOpen-addressing search along a probe order.
def openSearch {S K : Type*} [DecidableEq K] (T : S → Option K)
(order : List S) (k : K) : Bool :=
scanFind T k order
Open-addressing insert along a probe order: place the key in the first empty
slot. If the probe order has no empty slot the table is returned unchanged (the
table-full junk value; totality over Option-slot tables).
def openInsert {S K : Type*} [DecidableEq S] [DecidableEq K] (T : S → Option K)
(order : List S) (k : K) : S → Option K :=
match scanInsertPos T order with
| some s => Function.update T s (some k)
| none => TModel correctness (CLRS §11.4 insert/search behaviour)
If a key occupies no slot, an open-addressing search along any probe order fails.
theorem scanFind_absent {S K : Type*} [DecidableEq K] (T : S → Option K) (k : K)
(h : ∀ s, T s ≠ some k) (order : List S) : scanFind T k order = false := by
induction order with
| nil => rfl
| cons s rest ih =>
simp only [scanFind, if_neg (h s)]
by_cases he : T s = none
· simp [he]
· simp only [if_neg he]; exact ih
The first empty slot reported by scanInsertPos is a member of the probe
order.
theorem scanInsertPos_mem {S K : Type*} [DecidableEq K] (T : S → Option K)
(order : List S) (s : S) (h : scanInsertPos T order = some s) : s ∈ order := by
induction order with
| nil => simp [scanInsertPos] at h
| cons a rest ih =>
simp only [scanInsertPos] at h
by_cases ha : T a = none
· rw [if_pos ha, Option.some_inj] at h
subst h; simp
· rw [if_neg ha] at h
exact List.mem_cons_of_mem _ (ih h)
If the probe order has an empty slot, scanInsertPos reports one.
theorem scanInsertPos_isSome_of_empty {S K : Type*} [DecidableEq K]
(T : S → Option K) (order : List S) (h : ∃ s ∈ order, T s = none) :
∃ s, scanInsertPos T order = some s := by
induction order with
| nil => obtain ⟨s, hs, _⟩ := h; simp at hs
| cons a rest ih =>
by_cases ha : T a = none
· exact ⟨a, by simp [scanInsertPos, ha]⟩
· obtain ⟨s, hs, hTs⟩ := h
rw [List.mem_cons] at hs
rcases hs with rfl | hmem
· exact absurd hTs ha
· obtain ⟨s', hs'⟩ := ih ⟨s, hmem, hTs⟩
exact ⟨s', by simp only [scanInsertPos, if_neg ha]; exact hs'⟩After inserting a key into the first empty slot of a duplicate-free probe order, a search along that order finds it.
theorem scanFind_update_of_scanInsertPos {S K : Type*} [DecidableEq S]
[DecidableEq K] (T : S → Option K) (k : K) (order : List S) (s : S)
(hnd : order.Nodup) (hins : scanInsertPos T order = some s) :
scanFind (Function.update T s (some k)) k order = true := by
induction order with
| nil => simp [scanInsertPos] at hins
| cons a rest ih =>
simp only [scanInsertPos] at hins
by_cases ha : T a = none
· rw [if_pos ha, Option.some_inj] at hins
subst hins
simp [scanFind, Function.update_self]
· rw [if_neg ha] at hins
have hmem : s ∈ rest := scanInsertPos_mem T rest s hins
have hnd' := List.nodup_cons.mp hnd
have has : a ≠ s := fun hEq => hnd'.1 (hEq ▸ hmem)
simp only [scanFind, Function.update_of_ne has]
by_cases hak : T a = some k
· rw [if_pos hak]
· rw [if_neg hak, if_neg ha]
exact ih hnd'.2 hinsAC-1 (absent key not found). A key stored in no slot is not found by an open-addressing search along any probe order.
theorem openSearch_eq_false_of_absent {S K : Type*} [DecidableEq K]
(T : S → Option K) (order : List S) (k : K) (h : ∀ s, T s ≠ some k) :
openSearch T order k = false :=
scanFind_absent T k h orderAC-1 (inserted key found). If a duplicate-free probe order has an empty slot, then after inserting a key it is found by a search along the same order.
theorem openSearch_openInsert {S K : Type*} [DecidableEq S] [DecidableEq K]
(T : S → Option K) (order : List S) (k : K) (hnd : order.Nodup)
(hempty : ∃ s ∈ order, T s = none) :
openSearch (openInsert T order k) order k = true := by
obtain ⟨s, hs⟩ := scanInsertPos_isSome_of_empty T order hempty
have hupd : openInsert T order k = Function.update T s (some k) := by
simp only [openInsert, hs]
rw [openSearch, hupd]
exact scanFind_update_of_scanInsertPos T k order s hnd hsProbe schemes (CLRS §11.4, equations (11.5)-(11.7))
Each scheme is a function ZMod m → ZMod m mapping a probe number i to a slot,
for a fixed key. CLRS requires each probe sequence to enumerate all m slots
(be a permutation); linear probing always does, and double hashing does when the
step size is a unit modulo m.
Linear probing (CLRS equation (11.5)): h(k,i) = (h'(k) + i) mod m.
def linearProbe {m : ℕ} (h0 : ZMod m) (i : ZMod m) : ZMod m := h0 + i
Quadratic probing (CLRS equation (11.6)):
h(k,i) = (h'(k) + c₁ i + c₂ i²) mod m.
def quadraticProbe {m : ℕ} (h0 c1 c2 : ZMod m) (i : ZMod m) : ZMod m :=
h0 + c1 * i + c2 * i * i
Double hashing (CLRS equation (11.7)):
h(k,i) = (h₁(k) + i · h₂(k)) mod m.
def doubleHashProbe {m : ℕ} (h1 h2 : ZMod m) (i : ZMod m) : ZMod m := h1 + i * h2
Linear probing enumerates every slot: as a function of the probe number it is
a bijection of ZMod m (CLRS: a linear probe sequence is a permutation).
theorem linearProbe_bijective {m : ℕ} (h0 : ZMod m) :
Function.Bijective (linearProbe h0) := by
refine Function.bijective_iff_has_inverse.mpr ⟨fun t => t - h0, ?_, ?_⟩
· intro i; show (h0 + i) - h0 = i; ring
· intro t; show h0 + (t - h0) = t; ringLinear probing covers every slot (surjectivity form of the permutation property).
theorem linearProbe_surjective {m : ℕ} (h0 : ZMod m) :
Function.Surjective (linearProbe h0) :=
(linearProbe_bijective h0).surjective
Double hashing enumerates every slot when the step h₂ is a unit modulo m
(CLRS: h₂(k) must be relatively prime to m for the probe sequence to be a
permutation).
theorem doubleHashProbe_bijective {m : ℕ} (h1 h2 : ZMod m) (hu : IsUnit h2) :
Function.Bijective (doubleHashProbe h1 h2) := by
obtain ⟨u, rfl⟩ := hu
refine Function.bijective_iff_has_inverse.mpr ⟨fun t => (t - h1) * ↑u⁻¹, ?_, ?_⟩
· intro i
show ((doubleHashProbe h1 (↑u) i) - h1) * ↑u⁻¹ = i
have hu1 : (↑u : ZMod m) * ↑u⁻¹ = 1 := u.mul_inv
unfold doubleHashProbe
calc (h1 + i * ↑u - h1) * ↑u⁻¹ = i * (↑u * ↑u⁻¹) := by ring
_ = i := by rw [hu1, mul_one]
· intro t
show doubleHashProbe h1 (↑u) ((t - h1) * ↑u⁻¹) = t
have hu2 : (↑u⁻¹ : ZMod m) * ↑u = 1 := u.inv_mul
unfold doubleHashProbe
calc h1 + (t - h1) * ↑u⁻¹ * ↑u = h1 + (t - h1) * (↑u⁻¹ * ↑u) := by ring
_ = t := by rw [hu2, mul_one]; ringDouble hashing covers every slot when the step is a unit.
theorem doubleHashProbe_surjective {m : ℕ} (h1 h2 : ZMod m) (hu : IsUnit h2) :
Function.Surjective (doubleHashProbe h1 h2) :=
(doubleHashProbe_bijective h1 h2 hu).surjective
Quadratic probing starts at the base slot h'(k) (probe number 0).
theorem quadraticProbe_zero {m : ℕ} (h0 c1 c2 : ZMod m) :
quadraticProbe h0 c1 c2 0 = h0 := by
unfold quadraticProbe; ringExpected number of probes under uniform hashing (CLRS Theorems 11.6-11.8)
Under the uniform-hashing assumption the probe sequence of each key is equally
likely to be any of the m! permutations of the slots. For an unsuccessful
search with n occupied slots, the probability that the first i probes all hit
occupied slots is the without-replacement product ∏_{j<i} (n-j)/(m-j). The
expected number of probes is the tail-sum E[X] = ∑_{i≥0} P[X > i], which we
bound by the geometric series ∑_i α^i = 1/(1-α) via CLRS's per-factor bound
(n-j)/(m-j) ≤ n/m.
The load factor α = n/m of an open-addressing table.
noncomputable def openLoadFactor (m n : ℕ) : ℝ := (n : ℝ) / (m : ℝ)
Under uniform hashing, probeTail m n i is the probability that the first i
probes of an unsuccessful search all hit occupied slots: the without-replacement
product ∏_{j<i} (n-j)/(m-j) (CLRS §11.4, the factors leading to Theorem 11.6).
noncomputable def probeTail (m n i : ℕ) : ℝ :=
∏ j ∈ Finset.range i, ((n : ℝ) - (j : ℝ)) / ((m : ℝ) - (j : ℝ))
No probe is needed with certainty: probeTail _ _ 0 = 1.
The one-step recurrence of the tail probability.
theorem probeTail_succ (m n i : ℕ) :
probeTail m n (i + 1)
= probeTail m n i * (((n : ℝ) - (i : ℝ)) / ((m : ℝ) - (i : ℝ))) := by
simp only [probeTail, Finset.prod_range_succ]
The tail probabilities are nonnegative (for i ≤ m, where all denominators
are positive).
theorem probeTail_nonneg (m n : ℕ) (hnm : n ≤ m) (i : ℕ) (hi : i ≤ m) :
0 ≤ probeTail m n i := by
by_cases hin : i ≤ n
· apply Finset.prod_nonneg
intro j hj
have hji : j < i := Finset.mem_range.mp hj
have hjn : (j : ℝ) ≤ (n : ℝ) := by
have : j ≤ n := le_trans (Nat.le_of_lt hji) hin
exact_mod_cast this
have hjm : (j : ℝ) < (m : ℝ) := by
have : j < m := lt_of_lt_of_le hji (le_trans hin hnm)
exact_mod_cast this
apply div_nonneg <;> linarith
· have hni : n < i := not_le.mp hin
have hmem : n ∈ Finset.range i := Finset.mem_range.mpr hni
have hz : probeTail m n i = 0 := by
rw [probeTail]; exact Finset.prod_eq_zero hmem (by simp)
simp [hz]
CLRS per-factor bound. Each tail probability is at most α^i
(α = n/m), because (n-j)/(m-j) ≤ n/m. This is the heart of Theorem 11.6.
theorem probeTail_le_pow (m n : ℕ) (hnm : n ≤ m) (i : ℕ) (hi : i ≤ m) :
probeTail m n i ≤ (openLoadFactor m n) ^ i := by
induction i with
| zero => rw [probeTail_zero, pow_zero]
| succ i ih =>
have hile : i ≤ m := le_of_lt (Nat.lt_of_succ_le hi)
have him : i < m := Nat.lt_of_succ_le hi
have hprev : probeTail m n i ≤ (openLoadFactor m n) ^ i := ih hile
have hptnn : 0 ≤ probeTail m n i := probeTail_nonneg m n hnm i hile
have hmpos : (0 : ℝ) < (m : ℝ) := by
have : 0 < m := lt_of_le_of_lt (Nat.zero_le i) him
exact_mod_cast this
have hden : (0 : ℝ) < (m : ℝ) - (i : ℝ) := by
have : (i : ℝ) < (m : ℝ) := by exact_mod_cast him
linarith
have hαnn : 0 ≤ openLoadFactor m n := by
rw [openLoadFactor]; exact div_nonneg (by positivity) (by positivity)
rw [probeTail_succ, pow_succ]
by_cases hin : i < n
· have hile_n : (i : ℝ) ≤ (n : ℝ) := by exact_mod_cast Nat.le_of_lt hin
have hfnn : 0 ≤ ((n : ℝ) - (i : ℝ)) / ((m : ℝ) - (i : ℝ)) :=
div_nonneg (by linarith) (le_of_lt hden)
have hfle : ((n : ℝ) - (i : ℝ)) / ((m : ℝ) - (i : ℝ)) ≤ openLoadFactor m n := by
rw [openLoadFactor, div_le_div_iff₀ hden hmpos]
have hnr : (n : ℝ) ≤ (m : ℝ) := by exact_mod_cast hnm
have hir : (0 : ℝ) ≤ (i : ℝ) := by positivity
nlinarith [mul_le_mul_of_nonneg_right hnr hir]
exact mul_le_mul hprev hfle hfnn (pow_nonneg hαnn i)
· have hni : n ≤ i := not_lt.mp hin
have hnum : (n : ℝ) - (i : ℝ) ≤ 0 := by
have : (n : ℝ) ≤ (i : ℝ) := by exact_mod_cast hni
linarith
have hinv : 0 ≤ ((m : ℝ) - (i : ℝ))⁻¹ := inv_nonneg.mpr (le_of_lt hden)
have hfnp : ((n : ℝ) - (i : ℝ)) / ((m : ℝ) - (i : ℝ)) ≤ 0 := by
rw [div_eq_mul_inv]; nlinarith [hnum, hinv]
have hprod : probeTail m n i * (((n : ℝ) - (i : ℝ)) / ((m : ℝ) - (i : ℝ))) ≤ 0 := by
nlinarith [hptnn, hfnp]
have hpow : 0 ≤ (openLoadFactor m n) ^ i * openLoadFactor m n :=
mul_nonneg (pow_nonneg hαnn i) hαnn
linarith
A partial geometric sum is bounded by the full geometric series
∑_i α^i ≤ 1/(1-α) for 0 ≤ α < 1.
theorem geom_sum_le_inv (α : ℝ) (h0 : 0 ≤ α) (h1 : α < 1) (N : ℕ) :
∑ i ∈ Finset.range N, α ^ i ≤ 1 / (1 - α) := by
have hpos : (0 : ℝ) < 1 - α := by linarith
have hid : (∑ i ∈ Finset.range N, α ^ i) * (1 - α) = 1 - α ^ N := by
have h := geom_sum_mul α N
linear_combination (-1 : ℝ) * h
rw [le_div_iff₀ hpos, hid]
have hpN : (0 : ℝ) ≤ α ^ N := pow_nonneg h0 N
linarith
The expected number of probes for an unsuccessful search under uniform
hashing, as the tail-sum E[X] = ∑_{i} P[X > i] of the probe count X.
noncomputable def expectedUnsuccessfulProbes (m n : ℕ) : ℝ :=
∑ i ∈ Finset.range (m + 1), probeTail m n i
Theorem 11.6 (unsuccessful search). Under uniform hashing the expected
number of probes in an unsuccessful search is at most 1/(1-α) (α = n/m < 1),
proved as the tail-sum bounded by the geometric series.
theorem expectedUnsuccessfulProbes_le (m n : ℕ) (hn : n < m) :
expectedUnsuccessfulProbes m n ≤ 1 / (1 - openLoadFactor m n) := by
have hnm : n ≤ m := le_of_lt hn
have hmpos : 0 < m := lt_of_le_of_lt (Nat.zero_le n) hn
have hmr : (0 : ℝ) < (m : ℝ) := by exact_mod_cast hmpos
have hα0 : 0 ≤ openLoadFactor m n :=
div_nonneg (by positivity) (by positivity)
have hα1 : openLoadFactor m n < 1 := by
rw [openLoadFactor, div_lt_one hmr]; exact_mod_cast hn
calc expectedUnsuccessfulProbes m n
= ∑ i ∈ Finset.range (m + 1), probeTail m n i := rfl
_ ≤ ∑ i ∈ Finset.range (m + 1), (openLoadFactor m n) ^ i := by
apply Finset.sum_le_sum
intro i hi
exact probeTail_le_pow m n hnm i (Nat.lt_succ_iff.mp (Finset.mem_range.mp hi))
_ ≤ 1 / (1 - openLoadFactor m n) := geom_sum_le_inv _ hα0 hα1 (m + 1)
Corollary 11.7 (insertion). Inserting a key probes exactly as an
unsuccessful search does, so its expected number of probes is at most 1/(1-α).
theorem expectedInsertionProbes_le (m n : ℕ) (hn : n < m) :
expectedUnsuccessfulProbes m n ≤ 1 / (1 - openLoadFactor m n) :=
expectedUnsuccessfulProbes_le m n hn
The expected number of probes for a successful search under uniform
hashing: averaging, over the n insertion times j = 0, …, n-1, the expected
unsuccessful-search cost in a table already holding j keys (CLRS proof of
Theorem 11.8: the (j+1)-st key's probe cost equals an unsuccessful search among
j keys).
noncomputable def expectedSuccessfulProbes (m n : ℕ) : ℝ :=
(1 / (n : ℝ)) * ∑ j ∈ Finset.range n, expectedUnsuccessfulProbes m j
Theorem 11.8 (successful search), harmonic form. Under uniform hashing
the expected number of probes in a successful search is at most
(1/α) * ∑_{j<n} 1/(m-j) = (1/α)(H_m - H_{m-n}) (α = n/m). The stated
harmonic-sum bound is proved; the classical (1/α) ln(1/(1-α)) follows via
sum_inv_shift_le_log and is proved as expectedSuccessfulProbes_le_ln.
theorem expectedSuccessfulProbes_le (m n : ℕ) (hn : n ≤ m) (hnpos : 0 < n) :
expectedSuccessfulProbes m n
≤ (1 / openLoadFactor m n) * ∑ j ∈ Finset.range n, 1 / ((m : ℝ) - (j : ℝ)) := by
have hmpos : 0 < m := lt_of_lt_of_le hnpos hn
have hmpos' : (0 : ℝ) < (m : ℝ) := by exact_mod_cast hmpos
have step1 : expectedSuccessfulProbes m n
≤ (1 / (n : ℝ)) * ∑ j ∈ Finset.range n, 1 / (1 - openLoadFactor m j) := by
unfold expectedSuccessfulProbes
apply mul_le_mul_of_nonneg_left _ (by positivity)
apply Finset.sum_le_sum
intro j hj
exact expectedUnsuccessfulProbes_le m j (lt_of_lt_of_le (Finset.mem_range.mp hj) hn)
have hrw : ∀ j ∈ Finset.range n,
1 / (1 - openLoadFactor m j) = (m : ℝ) * (1 / ((m : ℝ) - (j : ℝ))) := by
intro j hj
have hjm : j < m := lt_of_lt_of_le (Finset.mem_range.mp hj) hn
have hjmr : (j : ℝ) < (m : ℝ) := by exact_mod_cast hjm
have hm0 : (m : ℝ) ≠ 0 := ne_of_gt hmpos'
have hsub : (1 : ℝ) - (j : ℝ) / (m : ℝ) = ((m : ℝ) - (j : ℝ)) / (m : ℝ) := by
rw [sub_div, div_self hm0]
rw [openLoadFactor, hsub, one_div_div, mul_one_div]
rw [Finset.sum_congr rfl hrw, ← Finset.mul_sum] at step1
have hfin : (1 / (n : ℝ)) * ((m : ℝ) * ∑ j ∈ Finset.range n, 1 / ((m : ℝ) - (j : ℝ)))
= (1 / openLoadFactor m n) * ∑ j ∈ Finset.range n, 1 / ((m : ℝ) - (j : ℝ)) := by
rw [openLoadFactor, one_div_div]; ring
rw [hfin] at step1
exact step1
The free-capacity identity 1/(1 - n/m) = m/(m-n) for a non-full table
(n < m): the reciprocal of the free capacity equals the ratio of the table size
to the remaining slots. This is the ln(1/(1-α)) = ln(m/(m-n)) identity used to
pass from the harmonic sum bound to the logarithmic form of Theorem 11.8.
lemma openLoadFactor_inv_eq (m n : ℕ) (hn : n < m) :
(1 : ℝ) / (1 - (n : ℝ) / (m : ℝ)) = (m : ℝ) / ((m - n : ℕ) : ℝ) := by
have hmpos : (0 : ℝ) < (m : ℝ) := by exact_mod_cast (lt_of_le_of_lt (Nat.zero_le n) hn)
have hmne : (m : ℝ) ≠ 0 := ne_of_gt hmpos
have hmncast : ((m - n : ℕ) : ℝ) = (m : ℝ) - (n : ℝ) := by
rw [Nat.cast_sub (le_of_lt hn)]
rw [hmncast]
have hsub : (m : ℝ) - (n : ℝ) ≠ 0 := by
have : (n : ℝ) < (m : ℝ) := by exact_mod_cast hn
linarith
have hαne : 1 - (n : ℝ) / (m : ℝ) ≠ 0 := by
have hα : (n : ℝ) / (m : ℝ) < 1 := (div_lt_one hmpos).2 (by exact_mod_cast hn)
linarith
field_simp [hmne, hsub, hαne]
Harmonic-sum bound. For n < m, ∑_{j<n} 1/(m-j) ≤ ln(m/(m-n)): each
term 1/(m-j) is at most ln((m-j)/(m-j-1)) (from the bound
ln(1+x) ≥ x/(1+x) with x = 1/(m-j-1)), and the resulting sum telescopes to
ln(m/(m-n)). This is the integral bound used to obtain the logarithmic form of
Theorem 11.8 from its harmonic form.
lemma sum_inv_shift_le_log (m n : ℕ) (hn : n < m) :
(∑ j ∈ Finset.range n, (1 : ℝ) / ((m : ℝ) - (j : ℝ)))
≤ Real.log ((m : ℝ) / ((m - n : ℕ) : ℝ)) := by
classical
have hterm (j : ℕ) (hj : j < n) : (1 : ℝ) / ((m : ℝ) - (j : ℝ))
≤ Real.log (((m : ℝ) - (j : ℝ)) / (((m : ℝ) - (j : ℝ)) - 1)) := by
have hsucc : j + 1 < m := by omega
have hjm1 : (j : ℝ) + 1 < (m : ℝ) := by exact_mod_cast hsucc
have hden : 0 < ((m : ℝ) - (j : ℝ)) - 1 := by linarith
have hnum : 0 < (m : ℝ) - (j : ℝ) := by linarith
have hx : 0 < ((m : ℝ) - (j : ℝ)) / (((m : ℝ) - (j : ℝ)) - 1) := div_pos hnum hden
have h := Real.one_sub_inv_le_log_of_pos hx
have hlin : (1 : ℝ) - (((m : ℝ) - (j : ℝ)) / (((m : ℝ) - (j : ℝ)) - 1))⁻¹
= (1 : ℝ) / ((m : ℝ) - (j : ℝ)) := by
field_simp [hnum.ne', hden.ne']
ring
rwa [hlin] at h
calc
(∑ j ∈ Finset.range n, (1 : ℝ) / ((m : ℝ) - (j : ℝ)))
≤ ∑ j ∈ Finset.range n,
Real.log (((m : ℝ) - (j : ℝ)) / (((m : ℝ) - (j : ℝ)) - 1)) := by
apply Finset.sum_le_sum
intro j hj
exact hterm j (Finset.mem_range.mp hj)
_ = Real.log ((m : ℝ) / ((m - n : ℕ) : ℝ)) := by
have hsplit : ∀ j ∈ Finset.range n,
Real.log (((m : ℝ) - (j : ℝ)) / (((m : ℝ) - (j : ℝ)) - 1))
= Real.log ((m : ℝ) - (j : ℝ)) - Real.log (((m : ℝ) - (j : ℝ)) - 1) := by
intro j hj
have hjn : j < n := Finset.mem_range.mp hj
have hsucc : j + 1 < m := by omega
have hjm1 : (j : ℝ) + 1 < (m : ℝ) := by exact_mod_cast hsucc
have hden : 0 < ((m : ℝ) - (j : ℝ)) - 1 := by linarith
have hnum : 0 < (m : ℝ) - (j : ℝ) := by linarith
exact Real.log_div hnum.ne' hden.ne'
rw [Finset.sum_congr rfl hsplit]
have htel : (∑ j ∈ Finset.range n,
(Real.log ((m : ℝ) - (j : ℝ)) - Real.log (((m : ℝ) - (j : ℝ)) - 1)))
= Real.log ((m : ℝ) - (0 : ℝ)) - Real.log ((m : ℝ) - (n : ℝ)) := by
simpa [Nat.cast_add, Nat.cast_one, sub_add_eq_sub_sub] using
(Finset.sum_range_sub' (f := fun i : ℕ => Real.log ((m : ℝ) - (i : ℝ))) n)
rw [htel, sub_zero, ← Nat.cast_sub (le_of_lt hn)]
have hm : (m : ℝ) ≠ 0 := by exact_mod_cast (ne_of_gt (lt_of_le_of_lt (Nat.zero_le n) hn))
have hmn : ((m - n : ℕ) : ℝ) ≠ 0 := by exact_mod_cast (ne_of_gt (Nat.sub_pos_of_lt hn))
rw [← Real.log_div hm hmn]
Theorem 11.8 (successful search), logarithmic form. Under uniform
hashing the expected number of probes in a successful search is at most
(1/α) * ln(1/(1-α)) (α = n/m < 1), obtained from the harmonic form
expectedSuccessfulProbes_le via the integral bound
∑_{j<n} 1/(m-j) ≤ ln(m/(m-n)) = ln(1/(1-α)).
theorem expectedSuccessfulProbes_le_ln (m n : ℕ) (hn : n < m) (hnpos : 0 < n) :
expectedSuccessfulProbes m n
≤ (1 / openLoadFactor m n) * Real.log (1 / (1 - openLoadFactor m n)) := by
have hharm := expectedSuccessfulProbes_le m n (le_of_lt hn) hnpos
have hlog : (∑ j ∈ Finset.range n, (1 : ℝ) / ((m : ℝ) - (j : ℝ)))
≤ Real.log ((m : ℝ) / ((m - n : ℕ) : ℝ)) := sum_inv_shift_le_log m n hn
have hαpos : 0 < openLoadFactor m n := by
rw [openLoadFactor]
exact div_pos (by exact_mod_cast hnpos) (by exact_mod_cast (lt_of_le_of_lt (Nat.zero_le n) hn))
have hαnonneg : 0 ≤ 1 / openLoadFactor m n := by
exact one_div_nonneg.mpr (le_of_lt hαpos)
have hconv : Real.log (1 / (1 - openLoadFactor m n)) = Real.log ((m : ℝ) / ((m - n : ℕ) : ℝ)) := by
congr 1
rw [openLoadFactor]
exact openLoadFactor_inv_eq m n hn
have hmult := mul_le_mul_of_nonneg_left hlog hαnonneg
calc
expectedSuccessfulProbes m n
≤ (1 / openLoadFactor m n) * ∑ j ∈ Finset.range n, 1 / ((m : ℝ) - (j : ℝ)) := hharm
_ ≤ (1 / openLoadFactor m n) * Real.log ((m : ℝ) / ((m - n : ℕ) : ℝ)) := hmult
_ = (1 / openLoadFactor m n) * Real.log (1 / (1 - openLoadFactor m n)) := by rw [hconv]end Chapter11end CLRSDefinitions and proofs
CLRSLean.FourthEdition.Chapter_11.Section_11_4_Open_Addressing.UniformProbe.Counting
CLRS Section 11.4 - Counting uniform probe prefixes
We restrict a slot permutation to its first i positions. The symmetric
group acts transitively on these prefix embeddings, so every embedding has the
same number of permutation extensions. Counting all embeddings determines
that extension count and then the number whose range lies in the occupied set.
namespace CLRSnamespace Chapter11
Inclusion of the first i probe positions into a table of size
m.
def probePrefixEmbedding {m i : Nat} (hi : i ≤ m) : Fin i ↪ Fin m :=
Fin.castLEEmb hi
Restriction of a full probe permutation to its first i positions.
def probePermutationPrefix {m i : Nat} (hi : i ≤ m)
(σ : Equiv.Perm (Fin m)) : Fin i ↪ Fin m :=
(probePrefixEmbedding hi).trans σ.toEmbeddingLeft multiplication of permutations becomes the natural action on their prefix embeddings.
theorem probePermutationPrefix_mul {m i : Nat} (hi : i ≤ m)
(τ σ : Equiv.Perm (Fin m)) :
probePermutationPrefix hi (τ * σ) = τ • probePermutationPrefix hi σ := by
ext j
simp [probePermutationPrefix, probePrefixEmbedding,
Function.Embedding.smul_apply, Equiv.Perm.smul_def, Equiv.Perm.mul_apply]Every injective ordered prefix extends to a full slot permutation.
theorem probePermutationPrefix_surjective {m i : Nat} (hi : i ≤ m) :
Function.Surjective (probePermutationPrefix hi) := by
intro e
obtain ⟨τ, hτ⟩ := Equiv.Perm.exists_smul_eq_embedding
(probePrefixEmbedding hi) e
refine ⟨τ, ?_⟩
ext j
have hj := DFunLike.congr_fun hτ j
exact congrArg Fin.val hjFibers of prefix restriction have equal cardinality.
theorem probePermutationPrefix_fiber_card_eq {m i : Nat} (hi : i ≤ m)
(e₁ e₂ : Fin i ↪ Fin m) :
((Finset.univ : Finset (Equiv.Perm (Fin m))).filter
(fun σ => probePermutationPrefix hi σ = e₁)).card =
((Finset.univ : Finset (Equiv.Perm (Fin m))).filter
(fun σ => probePermutationPrefix hi σ = e₂)).card := by
classical
obtain ⟨τ, hτ⟩ := Equiv.Perm.exists_smul_eq_embedding e₁ e₂
have hτinv : τ⁻¹ • e₂ = e₁ := by
rw [← hτ]
simp
refine Finset.card_bij' (fun σ _ => τ * σ) (fun σ _ => τ⁻¹ * σ) ?_ ?_ ?_ ?_
· intro σ hσ
simp only [Finset.mem_filter, Finset.mem_univ, true_and] at hσ ⊢
rw [probePermutationPrefix_mul, hσ, hτ]
· intro σ hσ
simp only [Finset.mem_filter, Finset.mem_univ, true_and] at hσ ⊢
rw [probePermutationPrefix_mul, hσ, hτinv]
· intro σ hσ
simp
· intro σ hσ
simp
A prescribed injective prefix has exactly (m-i)! extensions to a
permutation of all table slots.
theorem probePermutationPrefix_fiber_card {m i : Nat} (hi : i ≤ m)
(e : Fin i ↪ Fin m) :
((Finset.univ : Finset (Equiv.Perm (Fin m))).filter
(fun σ => probePermutationPrefix hi σ = e)).card = (m - i).factorial := by
classical
let base : Fin i ↪ Fin m := probePrefixEmbedding hi
let fiberCard := ((Finset.univ : Finset (Equiv.Perm (Fin m))).filter
(fun σ => probePermutationPrefix hi σ = base)).card
have htotal : m.factorial = m.descFactorial i * fiberCard := by
have hpartition := Finset.card_eq_sum_card_fiberwise
(s := (Finset.univ : Finset (Equiv.Perm (Fin m))))
(t := (Finset.univ : Finset (Fin i ↪ Fin m)))
(f := probePermutationPrefix hi) (by simp)
rw [Finset.card_univ, Fintype.card_perm, Fintype.card_fin] at hpartition
calc
m.factorial = ∑ b : Fin i ↪ Fin m,
((Finset.univ : Finset (Equiv.Perm (Fin m))).filter
(fun σ => probePermutationPrefix hi σ = b)).card := hpartition
_ = ∑ _b : Fin i ↪ Fin m, fiberCard := by
apply Finset.sum_congr rfl
intro b hb
exact probePermutationPrefix_fiber_card_eq hi b base
_ = m.descFactorial i * fiberCard := by
simp [Fintype.card_embedding_eq]
have hfactorial : (m - i).factorial * m.descFactorial i = m.factorial :=
Nat.factorial_mul_descFactorial hi
have hprefixPos : 0 < m.descFactorial i := Nat.descFactorial_pos.mpr hi
have hbase : fiberCard = (m - i).factorial := by
apply Nat.eq_of_mul_eq_mul_left hprefixPos
calc
m.descFactorial i * fiberCard = m.factorial := htotal.symm
_ = m.descFactorial i * (m - i).factorial := by
rw [← hfactorial, Nat.mul_comm]
calc
((Finset.univ : Finset (Equiv.Perm (Fin m))).filter
(fun σ => probePermutationPrefix hi σ = e)).card = fiberCard :=
probePermutationPrefix_fiber_card_eq hi e base
_ = (m - i).factorial := hbaseRestrict the codomain of an embedding to a finite occupied set.
def embeddingIntoOccupied {m i : Nat} (occupied : Finset (Fin m))
(e : Fin i ↪ Fin m) (he : ∀ j, e j ∈ occupied) : Fin i ↪ occupied where
toFun j := ⟨e j, he j⟩
inj' _a _b hab := e.injective (Subtype.ext_iff.mp hab)@[simp] theorem embeddingIntoOccupied_val {m i : Nat} (occupied : Finset (Fin m))
(e : Fin i ↪ Fin m) (he : ∀ j, e j ∈ occupied) (j : Fin i) :
((embeddingIntoOccupied occupied e he j : occupied) : Fin m) = e j := rflNumber of injective ordered prefixes whose values all lie in a fixed occupied set.
theorem occupiedPrefixEmbedding_card {m i : Nat} (occupied : Finset (Fin m)) :
((Finset.univ : Finset (Fin i ↪ Fin m)).filter
(fun e => ∀ j, e j ∈ occupied)).card = occupied.card.descFactorial i := by
classical
calc
((Finset.univ : Finset (Fin i ↪ Fin m)).filter
(fun e => ∀ j, e j ∈ occupied)).card =
(Finset.univ : Finset (Fin i ↪ occupied)).card := by
refine Finset.card_bij
(fun e he => by
have he' : ∀ j, e j ∈ occupied := by
simpa only [Finset.mem_filter, Finset.mem_univ, true_and] using he
exact embeddingIntoOccupied occupied e he') ?_ ?_ ?_
· intro e he
simp
· intro e₁ h₁ e₂ h₂ heq
ext j
have hj := congrArg Subtype.val (DFunLike.congr_fun heq j)
have hfin : e₁ j = e₂ j := by
simpa [embeddingIntoOccupied] using hj
exact congrArg Fin.val hfin
· intro e he
let lifted : Fin i ↪ Fin m :=
{ toFun := fun j => e j
inj' := fun a b hab => e.injective (Subtype.ext hab) }
refine ⟨lifted, ?_, ?_⟩
· simp only [Finset.mem_filter, Finset.mem_univ, true_and]
intro j
exact (e j).property
· ext j
rfl
_ = occupied.card.descFactorial i := by
simp [Fintype.card_embedding_eq]The occupied-prefix event can be read from the restricted embedding.
theorem firstProbesOccupied_iff_prefix {m i : Nat} (occupied : Finset (Fin m))
(hi : i ≤ m) (σ : Equiv.Perm (Fin m)) :
firstProbesOccupied occupied i σ ↔
∀ j : Fin i, probePermutationPrefix hi σ j ∈ occupied := by
constructor
· intro h j
exact h (probePrefixEmbedding hi j) j.isLt
· intro h j hj
let k : Fin i := ⟨j.val, hj⟩
simpa [probePermutationPrefix, probePrefixEmbedding, k] using h k
Full probe permutations whose first i positions are occupied.
noncomputable def occupiedPrefixPermutations {m : Nat}
(occupied : Finset (Fin m)) (i : Nat) : Finset (Equiv.Perm (Fin m)) := by
classical
exact Finset.univ.filter (firstProbesOccupied occupied i)
Exact number of full probe permutations whose first i positions
are occupied.
theorem firstProbesOccupied_card {m i : Nat} (occupied : Finset (Fin m))
(hi : i ≤ m) :
(occupiedPrefixPermutations occupied i).card =
occupied.card.descFactorial i * (m - i).factorial := by
classical
let good := (Finset.univ : Finset (Fin i ↪ Fin m)).filter
(fun e => ∀ j, e j ∈ occupied)
have hfiber := Finset.sum_card_fiberwise_eq_card_filter
(Finset.univ : Finset (Equiv.Perm (Fin m))) good
(probePermutationPrefix hi)
have hfilter :
((Finset.univ : Finset (Equiv.Perm (Fin m))).filter
(fun σ => probePermutationPrefix hi σ ∈ good)) =
occupiedPrefixPermutations occupied i := by
ext σ
simp only [Finset.mem_filter, Finset.mem_univ, true_and, good,
occupiedPrefixPermutations]
exact (firstProbesOccupied_iff_prefix occupied hi σ).symm
rw [hfilter] at hfiber
calc
(occupiedPrefixPermutations occupied i).card =
∑ e ∈ good,
((Finset.univ : Finset (Equiv.Perm (Fin m))).filter
(fun σ => probePermutationPrefix hi σ = e)).card := hfiber.symm
_ = ∑ _e ∈ good, (m - i).factorial := by
apply Finset.sum_congr rfl
intro e he
exact probePermutationPrefix_fiber_card hi e
_ = good.card * (m - i).factorial := by simp
_ = occupied.card.descFactorial i * (m - i).factorial := by
rw [show good.card = occupied.card.descFactorial i by
exact occupiedPrefixEmbedding_card occupied]end Chapter11end CLRSCLRSLean.FourthEdition.Chapter_11.Section_11_4_Open_Addressing.UniformProbe.Definitions
CLRS Section 11.4 - Uniform probe-space definitions
The sample space is the finite type of permutations of the table slots. A permutation sends probe positions to distinct table slots, exactly matching the uniform-hashing assumption used in CLRS Theorems 11.6--11.8.
namespace CLRSnamespace Chapter11open Probability
The first i positions of a probe permutation all hit occupied slots.
def firstProbesOccupied {m : Nat} (occupied : Finset (Fin m)) (i : Nat)
(σ : Equiv.Perm (Fin m)) : Prop :=
∀ j : Fin m, j.val < i → σ j ∈ occupiedIndicator of the occupied-prefix event with its classical decision procedure fixed inside the definition.
noncomputable def firstProbesOccupiedIndicator {m : Nat}
(occupied : Finset (Fin m)) (i : Nat) (σ : Equiv.Perm (Fin m)) : Real := by
classical
exact indicator (firstProbesOccupied occupied i σ)Uniform probability of an occupied prefix in the explicit permutation sample space.
noncomputable def uniformProbeTailProbability {m : Nat}
(occupied : Finset (Fin m)) (i : Nat) : Real := by
classical
exact fintypeExpect (firstProbesOccupiedIndicator occupied i)Number of probes made before an unsuccessful search reaches its first empty slot, including that final empty-slot probe. The definition is the finite tail sum of the concrete permutation execution.
noncomputable def uniformUnsuccessfulProbeCount {m : Nat} (occupied : Finset (Fin m))
(σ : Equiv.Perm (Fin m)) : Nat := by
classical
exact ∑ i ∈ Finset.range (m + 1),
if firstProbesOccupied occupied i σ then 1 else 0
The explicit unsuccessful-search probe count is bounded by the number of
its m + 1 possible tails.
theorem uniformUnsuccessfulProbeCount_le {m : Nat} (occupied : Finset (Fin m))
(σ : Equiv.Perm (Fin m)) :
uniformUnsuccessfulProbeCount occupied σ ≤ m + 1 := by
classical
unfold uniformUnsuccessfulProbeCount
calc
(∑ i ∈ Finset.range (m + 1), if firstProbesOccupied occupied i σ then 1 else 0)
≤ ∑ _i ∈ Finset.range (m + 1), 1 := by
apply Finset.sum_le_sum
intro i hi
split <;> omega
_ = m + 1 := by simpReal-cast form of the concrete tail-count definition.
theorem uniformUnsuccessfulProbeCount_cast {m : Nat} (occupied : Finset (Fin m))
(σ : Equiv.Perm (Fin m)) :
(uniformUnsuccessfulProbeCount occupied σ : Real) =
∑ i ∈ Finset.range (m + 1),
firstProbesOccupiedIndicator occupied i σ := by
classical
simp [uniformUnsuccessfulProbeCount, firstProbesOccupiedIndicator, indicator]
Canonical set consisting of the first n slots, used only to package
the successful-search average over insertion times.
def canonicalOccupied (m n : Nat) : Finset (Fin m) :=
Finset.univ.filter (fun x => x.val < n)The canonical occupied prefix has the requested cardinality when it fits inside the table.
theorem canonicalOccupied_card (m n : Nat) (hn : n ≤ m) :
(canonicalOccupied m n).card = n := by
classical
rw [canonicalOccupied]
have heq : Finset.univ.filter (fun x : Fin m => x.val < n) =
Finset.univ.map (Fin.castLEEmb hn) := by
ext x
simp only [Finset.mem_filter, Finset.mem_univ, true_and, Finset.mem_map]
constructor
· intro hx
exact ⟨⟨x.val, hx⟩, by simp⟩
· rintro ⟨y, _, rfl⟩
exact y.isLt
rw [heq, Finset.card_map, Finset.card_univ, Fintype.card_fin]
Explicit successful-search expectation: average the concrete unsuccessful
probe count at the n insertion-time loads.
noncomputable def uniformSuccessfulExpectedProbes (m n : Nat) : Real :=
(1 / (n : Real)) * ∑ j ∈ Finset.range n,
fintypeExpect (fun σ : Equiv.Perm (Fin m) =>
(uniformUnsuccessfulProbeCount (canonicalOccupied m j) σ : Real))end Chapter11end CLRSCLRSLean.FourthEdition.Chapter_11.Section_11_4_Open_Addressing.UniformProbe.Probability
CLRS Section 11.4 - Uniform permutation probabilities
The counting result is converted to the without-replacement product already used by the chapter. Finite-expectation linearity then identifies the concrete probe-count expectation with the existing tail sum and transports CLRS Theorems 11.6--11.8 to the explicit sample space.
namespace CLRSnamespace Chapter11open ProbabilityThe without-replacement product is the quotient of descending factorials.
theorem probeTail_eq_descFactorial_div (m n i : Nat) (hi : i ≤ m) :
probeTail m n i = (n.descFactorial i : Real) / (m.descFactorial i : Real) := by
by_cases hin : i ≤ n
· rw [probeTail, Nat.descFactorial_eq_prod_range, Nat.descFactorial_eq_prod_range,
Finset.prod_div_distrib]
congr 1
· rw [Nat.cast_prod]
apply Finset.prod_congr rfl
intro j hj
rw [Nat.cast_sub]
exact Nat.le_of_lt (lt_of_lt_of_le (Finset.mem_range.mp hj) hin)
· rw [Nat.cast_prod]
apply Finset.prod_congr rfl
intro j hj
rw [Nat.cast_sub]
exact Nat.le_of_lt (lt_of_lt_of_le (Finset.mem_range.mp hj) hi)
· have hni : n < i := Nat.lt_of_not_ge hin
have hnmem : n ∈ Finset.range i := Finset.mem_range.mpr hni
have hnum : n.descFactorial i = 0 := Nat.descFactorial_eq_zero_iff_lt.mpr hni
rw [probeTail, Finset.prod_eq_zero hnmem (by simp), hnum, Nat.cast_zero, zero_div]
The explicit event probability is its satisfying-permutation count divided
by m!.
theorem uniformProbeTailProbability_eq_card {m i : Nat}
(occupied : Finset (Fin m)) :
uniformProbeTailProbability occupied i =
((occupiedPrefixPermutations occupied i).card : Real) / (m.factorial : Real) := by
classical
unfold uniformProbeTailProbability Probability.fintypeExpect
rw [show (∑ σ : Equiv.Perm (Fin m), firstProbesOccupiedIndicator occupied i σ) =
((occupiedPrefixPermutations occupied i).card : Real) by
simp [firstProbesOccupiedIndicator, indicator, occupiedPrefixPermutations]]
simp [Fintype.card_perm]
The probability of an occupied prefix under a uniform slot permutation is
exactly the chapter's without-replacement product probeTail.
theorem uniformProbeTailProbability_eq_probeTail {m i : Nat}
(occupied : Finset (Fin m)) (hi : i ≤ m) :
uniformProbeTailProbability occupied i = probeTail m occupied.card i := by
rw [uniformProbeTailProbability_eq_card, firstProbesOccupied_card occupied hi]
have hfactorial : ((m.factorial : Nat) : Real) =
((m - i).factorial : Real) * (m.descFactorial i : Real) := by
exact_mod_cast (Nat.factorial_mul_descFactorial hi).symm
have hrest : ((m - i).factorial : Real) ≠ 0 := by positivity
have hprefix : ((m.descFactorial i : Nat) : Real) ≠ 0 := by
exact_mod_cast (Nat.ne_of_gt (Nat.descFactorial_pos.mpr hi))
rw [hfactorial, probeTail_eq_descFactorial_div m occupied.card i hi]
push_cast
field_simpThe expected concrete unsuccessful-search probe count is the existing tail-sum model.
theorem uniformUnsuccessfulExpectedProbes_eq {m : Nat}
(occupied : Finset (Fin m)) :
fintypeExpect (fun σ : Equiv.Perm (Fin m) =>
(uniformUnsuccessfulProbeCount occupied σ : Real)) =
expectedUnsuccessfulProbes m occupied.card := by
calc
fintypeExpect (fun σ : Equiv.Perm (Fin m) =>
(uniformUnsuccessfulProbeCount occupied σ : Real)) =
fintypeExpect (fun σ : Equiv.Perm (Fin m) =>
∑ i ∈ Finset.range (m + 1), firstProbesOccupiedIndicator occupied i σ) := by
apply congrArg fintypeExpect
funext σ
exact uniformUnsuccessfulProbeCount_cast occupied σ
_ = ∑ i ∈ Finset.range (m + 1),
fintypeExpect (firstProbesOccupiedIndicator occupied i) := by
exact fintypeExpect_sum (Finset.range (m + 1))
(fun i σ => firstProbesOccupiedIndicator occupied i σ)
_ = ∑ i ∈ Finset.range (m + 1), probeTail m occupied.card i := by
apply Finset.sum_congr rfl
intro i hi
exact uniformProbeTailProbability_eq_probeTail occupied
(Nat.lt_succ_iff.mp (Finset.mem_range.mp hi))
_ = expectedUnsuccessfulProbes m occupied.card := rflExplicit-sample-space form of CLRS Theorem 11.6.
theorem uniformUnsuccessfulExpectedProbes_le {m : Nat}
(occupied : Finset (Fin m)) (hnotFull : occupied.card < m) :
fintypeExpect (fun σ : Equiv.Perm (Fin m) =>
(uniformUnsuccessfulProbeCount occupied σ : Real)) ≤
1 / (1 - openLoadFactor m occupied.card) := by
rw [uniformUnsuccessfulExpectedProbes_eq]
exact expectedUnsuccessfulProbes_le m occupied.card hnotFullExplicit-sample-space form of CLRS Corollary 11.7 for insertion.
theorem uniformInsertionExpectedProbes_le {m : Nat}
(occupied : Finset (Fin m)) (hnotFull : occupied.card < m) :
fintypeExpect (fun σ : Equiv.Perm (Fin m) =>
(uniformUnsuccessfulProbeCount occupied σ : Real)) ≤
1 / (1 - openLoadFactor m occupied.card) :=
uniformUnsuccessfulExpectedProbes_le occupied hnotFullThe explicit successful-search average agrees with the chapter's insertion- time average.
theorem uniformSuccessfulExpectedProbes_eq (m n : Nat) (hn : n ≤ m) :
uniformSuccessfulExpectedProbes m n = expectedSuccessfulProbes m n := by
unfold uniformSuccessfulExpectedProbes expectedSuccessfulProbes
congr 1
apply Finset.sum_congr rfl
intro j hj
rw [uniformUnsuccessfulExpectedProbes_eq, canonicalOccupied_card]
exact le_trans (Nat.le_of_lt (Finset.mem_range.mp hj)) hnExplicit-sample-space logarithmic form of CLRS Theorem 11.8.
theorem uniformSuccessfulExpectedProbes_le_ln (m n : Nat)
(hn : n < m) (hnpos : 0 < n) :
uniformSuccessfulExpectedProbes m n ≤
(1 / openLoadFactor m n) * Real.log (1 / (1 - openLoadFactor m n)) := by
rw [uniformSuccessfulExpectedProbes_eq m n (le_of_lt hn)]
exact expectedSuccessfulProbes_le_ln m n hn hnposend Chapter11end CLRS