Imports
CLRS Section 18.1 - B-tree key count and height bound
This module counts every key slot represented by a B-tree. Its exact accounting identity rewrites an internal node's augmented key count as the sum of the augmented counts of its children. That recurrence is the arithmetic foundation for the structural minimum-key and logarithmic-height bounds.
namespace CLRSnamespace Chapter18namespace BTreeExact key accounting
The number of key slots represented by a B-tree.
A node's total key count is its local key count plus all child counts.
theorem totalKeys_node (ks : List Nat) (cs : List BTree) :
totalKeys (node ks cs) =
ks.length + (cs.map totalKeys).sum := by
unfold totalKeys
simp [keysOf, List.length_flatMap]For an internal node, adding one to the total key count exactly absorbs every separator key into one augmented count per child.
private lemma totalKeys_add_one_eq_sum_children
{ks : List Nat} {c0 : BTree} {cs : List BTree}
(hcb : ChildBounded (node ks (c0 :: cs))) :
totalKeys (node ks (c0 :: cs)) + 1 =
((c0 :: cs).map (fun child => totalKeys child + 1)).sum := by
have hlen : (c0 :: cs).length = ks.length + 1 := by
unfold ChildBounded at hcb
simpa using hcb.1
rw [totalKeys_node, List.sum_map_add, List.map_const', List.sum_const_nat]
simp only [Nat.mul_one]
omegaA pointwise lower bound lifts to the sum over all list positions.
private lemma length_mul_le_sum_map
{α : Type} (xs : List α) (q : Nat) (f : α → Nat)
(hpoint : ∀ x ∈ xs, q ≤ f x) :
xs.length * q ≤ (xs.map f).sum := by
have hsum : (xs.map (fun _ => q)).sum ≤ (xs.map f).sum :=
List.sum_le_sum hpoint
rw [List.map_const', List.sum_const_nat] at hsum
exact hsumInternal-child projections
Every child position of a child-bounded internal node is child-bounded.
private lemma childBounded_of_mem
{ks : List Nat} {c0 child : BTree} {cs : List BTree}
(hcb : ChildBounded (node ks (c0 :: cs)))
(hc : child ∈ c0 :: cs) :
ChildBounded child := by
unfold ChildBounded at hcb
exact hcb.2.2 child hcEvery child position of an occupied node satisfies non-root occupancy.
private lemma occupancy_false_of_mem
{t : Nat} {isRoot : Bool} {ks : List Nat} {c0 child : BTree}
{cs : List BTree}
(hocc : Occupancy t isRoot (node ks (c0 :: cs)))
(hc : child ∈ c0 :: cs) :
Occupancy t false child := by
unfold Occupancy at hocc
exact hocc.2.2.2 child hcEvery child position of a same-depth internal node is itself same-depth.
private lemma sameDepth_of_mem
{ks : List Nat} {c0 child : BTree} {cs : List BTree}
(hsd : SameDepth (node ks (c0 :: cs)))
(hc : child ∈ c0 :: cs) :
SameDepth child := by
rcases List.mem_cons.mp hc with rfl | hcTail
· exact sameDepth_head_sd hsd
· exact sameDepth_tail_sd hsd child hcTailEvery child of a same-depth internal node has the head child's height.
private lemma heightOf_eq_head_of_mem
{ks : List Nat} {c0 child : BTree} {cs : List BTree}
(hsd : SameDepth (node ks (c0 :: cs)))
(hc : child ∈ c0 :: cs) :
heightOf child = heightOf c0 :=
sameDepth_children_eq_height hsd child hc c0 (by simp)Non-root minimum-key bound
Every non-root B-tree subtree contains enough key slots for its height. The augmented form avoids natural-number subtraction and is the induction theorem used by the root-level CLRS bound.
theorem nonRoot_totalKeys_add_one_lower_bound
(t : Nat) (_ht : 2 ≤ t) {tr : BTree}
(hcb : ChildBounded tr)
(hocc : Occupancy t false tr)
(hsd : SameDepth tr) :
t ^ (heightOf tr + 1) ≤ totalKeys tr + 1 := by
induction hsd with
| leaf ks =>
simp [Occupancy] at hocc
simp [heightOf, totalKeys_node]
omega
| internal ks c0 cs hheights hsd0 hsdcs ih0 ihcs =>
have hsdNode : SameDepth (node ks (c0 :: cs)) :=
SameDepth.internal ks c0 cs hheights hsd0 hsdcs
have hcount : t ≤ (c0 :: cs).length := by
have hocc' := hocc
simp [Occupancy] at hocc'
rcases hocc' with ⟨_, _, ⟨hcount, _⟩, _, _⟩
exact hcount
let q := t ^ (heightOf c0 + 1)
have hpoint :
∀ child ∈ c0 :: cs, q ≤ totalKeys child + 1 := by
intro child hc
rcases List.mem_cons.mp hc with rfl | hcTail
· exact ih0
(childBounded_of_mem hcb (by simp))
(occupancy_false_of_mem hocc (by simp))
· have hcMem : child ∈ c0 :: cs := by simp [hcTail]
simpa [q, heightOf_eq_head_of_mem hsdNode hcMem] using
(ihcs child hcTail
(childBounded_of_mem hcb hcMem)
(occupancy_false_of_mem hocc hcMem))
have hsum :
(c0 :: cs).length * q ≤
((c0 :: cs).map (fun child => totalKeys child + 1)).sum :=
length_mul_le_sum_map (c0 :: cs) q
(fun child => totalKeys child + 1) hpoint
have hmul : t * q ≤ (c0 :: cs).length * q :=
Nat.mul_le_mul_right q hcount
rw [totalKeys_add_one_eq_sum_children hcb]
calc
t ^ (heightOf (node ks (c0 :: cs)) + 1) = t * q := by
rw [heightOf_internal_of_sameDepth hsdNode]
have hexponent :
1 + heightOf c0 + 1 = (heightOf c0 + 1) + 1 := by
omega
rw [hexponent, Nat.pow_succ]
exact Nat.mul_comm _ _
_ ≤ (c0 :: cs).length * q := hmul
_ ≤ ((c0 :: cs).map (fun child => totalKeys child + 1)).sum := hsumRoot minimum-key and logarithmic-height bounds
A well-formed root is either the legal empty tree or has the CLRS augmented minimum key count for its height.
theorem wellFormed_empty_or_totalKeys_add_one_lower_bound
(t : Nat) (ht : 2 ≤ t) {tr : BTree}
(hwf : WellFormed t tr) :
tr = node [] [] ∨
2 * t ^ heightOf tr ≤ totalKeys tr + 1 := by
rcases hwf with ⟨_hsorted, hcb, hocc, hsd⟩
cases tr with
| node ks children =>
cases children with
| nil =>
by_cases hks : ks = []
· left
simp [hks]
· right
simp [Occupancy, hks] at hocc
simp [heightOf, totalKeys_node]
omega
| cons c0 cs =>
right
have hcount : 2 ≤ (c0 :: cs).length := by
have hocc' := hocc
unfold Occupancy at hocc'
rcases hocc' with ⟨_, _, hchildren, _⟩
rcases hchildren with hchildrenEmpty | hchildrenBounds
· simp at hchildrenEmpty
· exact hchildrenBounds.1
let q := t ^ (heightOf c0 + 1)
have hpoint :
∀ child ∈ c0 :: cs, q ≤ totalKeys child + 1 := by
intro child hc
simpa [q, heightOf_eq_head_of_mem hsd hc] using
(nonRoot_totalKeys_add_one_lower_bound t ht
(childBounded_of_mem hcb hc)
(occupancy_false_of_mem hocc hc)
(sameDepth_of_mem hsd hc))
have hsum :
(c0 :: cs).length * q ≤
((c0 :: cs).map (fun child => totalKeys child + 1)).sum :=
length_mul_le_sum_map (c0 :: cs) q
(fun child => totalKeys child + 1) hpoint
have hmul : 2 * q ≤ (c0 :: cs).length * q :=
Nat.mul_le_mul_right q hcount
rw [totalKeys_add_one_eq_sum_children hcb]
calc
2 * t ^ heightOf (node ks (c0 :: cs)) = 2 * q := by
rw [heightOf_internal_of_sameDepth hsd]
simp [q, Nat.add_comm]
_ ≤ (c0 :: cs).length * q := hmul
_ ≤ ((c0 :: cs).map (fun child => totalKeys child + 1)).sum := hsumA well-formed tree is empty or satisfies the textbook minimum-key expression.
theorem wellFormed_empty_or_minKeys_le_totalKeys
(t : Nat) (ht : 2 ≤ t) {tr : BTree}
(hwf : WellFormed t tr) :
tr = node [] [] ∨ minKeys t (heightOf tr) ≤ totalKeys tr := by
rcases wellFormed_empty_or_totalKeys_add_one_lower_bound t ht hwf with
hempty | hbound
· exact Or.inl hempty
· right
unfold minKeys
omegaEvery nonempty well-formed tree satisfies the textbook minimum-key bound.
theorem wellFormed_minKeys_le_totalKeys
(t : Nat) (ht : 2 ≤ t) {tr : BTree}
(hwf : WellFormed t tr)
(hne : tr ≠ node [] []) :
minKeys t (heightOf tr) ≤ totalKeys tr := by
rcases wellFormed_empty_or_minKeys_le_totalKeys t ht hwf with
hempty | hbound
· exact (hne hempty).elim
· exact hboundThe height of every well-formed B-tree, including the empty tree, is at most the minimum-degree-base logarithm of its CLRS normalized key count.
theorem wellFormed_height_log_bound
(t : Nat) (ht : 2 ≤ t) {tr : BTree}
(hwf : WellFormed t tr) :
heightOf tr ≤ Nat.log t ((totalKeys tr + 1) / 2) := by
rcases wellFormed_empty_or_totalKeys_add_one_lower_bound t ht hwf with
hempty | hbound
· subst tr
simp [heightOf, totalKeys, keysOf]
· have htBase : 1 < t := by omega
apply Nat.le_log_of_pow_le htBase
have htwo : 0 < 2 := by omega
apply (Nat.le_div_iff_mul_le htwo).2
simpa [Nat.mul_comm] using hboundend BTreeend Chapter18end CLRS