(*
File: KD_Tree.thy
Author: Martin Rau, TU München
*)
section "Definition of the ‹k›-d Tree"
theory KD_Tree
imports
Complex_Main
"HOL-Analysis.Finite_Cartesian_Product"
"HOL-Analysis.Topology_Euclidean_Space"
begin
text ‹
A ‹k›-d tree is a space-partitioning data structure for organizing points in a ‹k›-dimensional space.
In principle the ‹k›-d tree is a binary tree. The leafs hold the ‹k›-dimensional points and the nodes
contain left and right subtrees as well as a discriminator ‹v› at a particular axis ‹k›.
Every node divides the space into two parts by splitting along a hyperplane.
Consider a node ‹n› with associated discriminator ‹v› at axis ‹k›.
All points in the left subtree must have a value at axis ‹k› that is less than or
equal to ‹v› and all points in the right subtree must have a value at axis ‹k› that is
greater than ‹v›.
Deviations from the papers:
The chosen tree representation is taken from cite‹"DBLP:journals/toms/FriedmanBF77"› with one minor
adjustment. Originally the leafs hold buckets of points of size ‹b›. This representation fixes the
bucket size to ‹b = 1›, a single point per Leaf. This is only a minor adjustment since the paper
proves that ‹b = 1› is the optimal bucket size for minimizing the running time of the nearest neighbor
algorithm cite‹"DBLP:journals/toms/FriedmanBF77"›, only simplifies building the optimized
‹k›-d trees cite‹"DBLP:journals/toms/FriedmanBF77"› and has little influence on the
search algorithm cite‹"DBLP:journals/cacm/Bentley75"›.
›
type_synonym 'k point = "(real, 'k) vec"
lemma dist_point_def:
fixes p0 :: "('k::finite) point"
shows "dist p0 p1 = sqrt (∑k ∈ UNIV. (p0$k - p1$k)2)"
unfolding dist_vec_def L2_set_def dist_real_def by simp
datatype 'k kdt =
Leaf "'k point"
| Node 'k real "'k kdt" "'k kdt"
subsection ‹Definition of the ‹k›-d Tree Invariant and Related Functions›
fun set_kdt :: "'k kdt → ('k point) set" where
"set_kdt (Leaf p) = { p }"
| "set_kdt (Node _ _ l r) = set_kdt l ∪ set_kdt r"
definition spread :: "('k::finite) → 'k point set → real" where
"spread k P = (if P = {} then 0 else let V = (λp. p$k) ` P in Max V - Min V)"
definition widest_spread_axis :: "('k::finite) → 'k set → 'k point set → bool" where
"widest_spread_axis k K ps ⟷ (∀k' ∈ K. spread k' ps ≤ spread k ps)"
fun invar :: "('k::finite) kdt → bool" where
"invar (Leaf p) ⟷ True"
| "invar (Node k v l r) ⟷ (∀p ∈ set_kdt l. p$k ≤ v) ∧ (∀p ∈ set_kdt r. v < p$k) ∧
widest_spread_axis k UNIV (set_kdt l ∪ set_kdt r) ∧ invar l ∧ invar r"
fun size_kdt :: "'k kdt → nat" where
"size_kdt (Leaf _) = 1"
| "size_kdt (Node _ _ l r) = size_kdt l + size_kdt r"
fun height :: "'k kdt → nat" where
"height (Leaf _) = 0"
| "height (Node _ _ l r) = max (height l) (height r) + 1"
fun min_height :: "'k kdt → nat" where
"min_height (Leaf _) = 0"
| "min_height (Node _ _ l r) = min (min_height l) (min_height r) + 1"
definition balanced :: "'k kdt → bool" where
"balanced kdt ⟷ height kdt - min_height kdt ≤ 1"
fun complete :: "'k kdt → bool" where
"complete (Leaf _) = True"
| "complete (Node _ _ l r) ⟷ complete l ∧ complete r ∧ height l = height r"
lemma invar_l:
"invar (Node k v l r) ==> invar l"
by simp
lemma invar_r:
"invar (Node k v l r) ==> invar r"
by simp
lemma invar_l_le_k:
"invar (Node k v l r) ==> ∀p ∈ set_kdt l. p$k ≤ v"
by simp
lemma invar_r_ge_k:
"invar (Node k v l r) ==> ∀p ∈ set_kdt r. v < p$k"
by simp
lemma invar_set:
"set_kdt (Node k v l r) = set_kdt l ∪ set_kdt r"
by simp
subsection ‹Lemmas adapted from ‹HOL-Library.Tree› to ‹k›-d Tree›
lemma size_ge0[simp]:
"0 < size_kdt kdt"
by (induction kdt) auto
lemma eq_size_1[simp]:
"size_kdt kdt = 1 ⟷ (∃p. kdt = Leaf p)"
apply (induction kdt)
apply (auto)
using size_ge0 nat_less_le apply blast+
done
lemma eq_1_size[simp]:
"1 = size_kdt kdt ⟷ (∃p. kdt = Leaf p)"
using eq_size_1 by metis
lemma neq_Leaf_iff:
"(∄p. kdt = Leaf p) = (∃k v l r. kdt = Node k v l r)"
by (cases kdt) auto
lemma eq_height_0[simp]:
"height kdt = 0 ⟷ (∃p. kdt = Leaf p)"
by (cases kdt) auto
lemma eq_0_height[simp]:
"0 = height kdt ⟷ (∃p. kdt = Leaf p)"
by (cases kdt) auto
lemma eq_min_height_0[simp]:
"min_height kdt = 0 ⟷ (∃p. kdt = Leaf p)"
by (cases kdt) auto
lemma eq_0_min_height[simp]:
"0 = min_height kdt ⟷ (∃p. kdt = Leaf p)"
by (cases kdt) auto
lemma size_height:
"size_kdt kdt ≤ 2 ^ height kdt"
proof(induction kdt)
case (Node k v l r)
show ?case
proof (cases "height l ≤ height r")
case True
have "size_kdt (Node k v l r) = size_kdt l + size_kdt r" by simp
also have "… ≤ 2 ^ height l + 2 ^ height r" using Node.IH by arith
also have "… ≤ 2 ^ height r + 2 ^ height r" using True by simp
also have "… = 2 ^ height (Node k v l r)"
using True by (auto simp: max_def mult_2)
finally show ?thesis .
next
case False
have "size_kdt (Node k v l r) = size_kdt l + size_kdt r" by simp
also have "… ≤ 2 ^ height l + 2 ^ height r" using Node.IH by arith
also have "… ≤ 2 ^ height l + 2 ^ height l" using False by simp
finally show ?thesis using False by (auto simp: max_def mult_2)
qed
qed simp
lemma min_height_le_height:
"min_height kdt ≤ height kdt"
by (induction kdt) auto
lemma min_height_size:
"2 ^ min_height kdt ≤ size_kdt kdt"
proof(induction kdt)
case (Node k v l r)
have "(2::nat) ^ min_height (Node k v l r) ≤ 2 ^ min_height l + 2 ^ min_height r"
by (simp add: min_def)
also have "… ≤ size_kdt (Node k v l r)" using Node.IH by simp
finally show ?case .
qed simp
lemma complete_iff_height:
"complete kdt ⟷ (min_height kdt = height kdt)"
apply (induction kdt)
apply simp
apply (simp add: min_def max_def)
by (metis le_antisym le_trans min_height_le_height)
lemma size_if_complete:
"complete kdt ==> size_kdt kdt = 2 ^ height kdt"
by (induction kdt) auto
lemma complete_if_size_height:
"size_kdt kdt = 2 ^ height kdt ==> complete kdt"
proof (induction "height kdt" arbitrary: kdt)
case 0 thus ?case by auto
next
case (Suc h)
hence "∄p. kdt = Leaf p"
by auto
then obtain k v l r where [simp]: "kdt = Node k v l r"
using neq_Leaf_iff by metis
have 1: "height l ≤ h" and 2: "height r ≤ h" using Suc(2) by(auto)
have 3: "¬ height l < h"
proof
assume 0: "height l < h"
have "size_kdt kdt = size_kdt l + size_kdt r" by simp
also have "… ≤ 2 ^ height l + 2 ^ height r"
using size_height[of l] size_height[of r] by arith
also have " … < 2 ^ h + 2 ^ height r" using 0 by (simp)
also have " … ≤ 2 ^ h + 2 ^ h" using 2 by (simp)
also have "… = 2 ^ (Suc h)" by (simp)
also have "… = size_kdt kdt" using Suc(2,3) by simp
finally have "size_kdt kdt < size_kdt kdt" .
thus False by (simp)
qed
have 4: "¬ height r < h"
proof
assume 0: "height r < h"
have "size_kdt kdt = size_kdt l + size_kdt r" by simp
also have "… ≤ 2 ^ height l + 2 ^ height r"
using size_height[of l] size_height[of r] by arith
also have " … < 2 ^ height l + 2 ^ h" using 0 by (simp)
also have " … ≤ 2 ^ h + 2 ^ h" using 1 by (simp)
also have "… = 2 ^ (Suc h)" by (simp)
also have "… = size_kdt kdt" using Suc(2,3) by simp
finally have "size_kdt kdt < size_kdt kdt" .
thus False by (simp)
qed
from 1 2 3 4 have *: "height l = h" "height r = h" by linarith+
hence "size_kdt l = 2 ^ height l" "size_kdt r = 2 ^ height r"
using Suc(3) size_height[of l] size_height[of r] by (auto)
with * Suc(1) show ?case by simp
qed
lemma complete_if_size_min_height:
"size_kdt kdt = 2 ^ min_height kdt ==> complete kdt"
proof (induct "min_height kdt" arbitrary: kdt)
case 0 thus ?case by auto
next
case (Suc h)
hence "∄p. kdt = Leaf p"
by auto
then obtain k v l r where [simp]: "kdt = Node k v l r"
using neq_Leaf_iff by metis
have 1: "h ≤ min_height l" and 2: "h ≤ min_height r" using Suc(2) by (auto)
have 3: "¬ h < min_height l"
proof
assume 0: "h < min_height l"
have "size_kdt kdt = size_kdt l + size_kdt r" by simp
also note min_height_size[of l]
also(xtrans) note min_height_size[of r]
also(xtrans) have "(2::nat) ^ min_height l > 2 ^ h"
using 0 by (simp add: diff_less_mono)
also(xtrans) have "(2::nat) ^ min_height r ≥ 2 ^ h" using 2 by simp
also(xtrans) have "(2::nat) ^ h + 2 ^ h = 2 ^ (Suc h)" by (simp)
also have "… = size_kdt kdt" using Suc(2,3) by simp
finally show False by (simp add: diff_le_mono)
qed
have 4: "¬ h < min_height r"
proof
assume 0: "h < min_height r"
have "size_kdt kdt = size_kdt l + size_kdt r" by simp
also note min_height_size[of l]
also(xtrans) note min_height_size[of r]
also(xtrans) have "(2::nat) ^ min_height r > 2 ^ h"
using 0 by (simp add: diff_less_mono)
also(xtrans) have "(2::nat) ^ min_height l ≥ 2 ^ h" using 1 by simp
also(xtrans) have "(2::nat) ^ h + 2 ^ h = 2 ^ (Suc h)" by (simp)
also have "… = size_kdt kdt" using Suc(2,3) by simp
finally show False by (simp add: diff_le_mono)
qed
from 1 2 3 4 have *: "min_height l = h" "min_height r = h" by linarith+
hence "size_kdt l = 2 ^ min_height l" "size_kdt r = 2 ^ min_height r"
using Suc(3) min_height_size[of l] min_height_size[of r] by (auto)
with * Suc(1) show ?case
by (simp add: complete_iff_height)
qed
lemma complete_iff_size:
"complete kdt ⟷ size_kdt kdt = 2 ^ height kdt"
using complete_if_size_height size_if_complete by blast
lemma size_height_if_incomplete:
"¬ complete kdt ==> size_kdt kdt < 2 ^ height kdt"
by (meson antisym_conv complete_iff_size not_le size_height)
lemma min_height_size_if_incomplete:
"¬ complete kdt ==> 2 ^ min_height kdt < size_kdt kdt"
by (metis complete_if_size_min_height le_less min_height_size)
lemma balanced_subtreeL:
"balanced (Node k v l r) ==> balanced l"
by (simp add: balanced_def)
lemma balanced_subtreeR:
"balanced (Node k v l r) ==> balanced r"
by (simp add: balanced_def)
lemma balanced_optimal:
assumes "balanced kdt" "size_kdt kdt ≤ size_kdt kdt'"
shows "height kdt ≤ height kdt'"
proof (cases "complete kdt")
case True
have "(2::nat) ^ height kdt ≤ 2 ^ height kdt'"
proof -
have "2 ^ height kdt = size_kdt kdt"
using True by (simp add: complete_iff_height size_if_complete)
also have "… ≤ size_kdt kdt'" using assms(2) by simp
also have "… ≤ 2 ^ height kdt'" by (rule size_height)
finally show ?thesis .
qed
thus ?thesis by (simp)
next
case False
have "(2::nat) ^ min_height kdt < 2 ^ height kdt'"
proof -
have "(2::nat) ^ min_height kdt < size_kdt kdt"
by(rule min_height_size_if_incomplete[OF False])
also have "… ≤ size_kdt kdt'" using assms(2) by simp
also have "… ≤ 2 ^ height kdt'" by(rule size_height)
finally have "(2::nat) ^ min_height kdt < (2::nat) ^ height kdt'" .
thus ?thesis .
qed
hence *: "min_height kdt < height kdt'" by simp
have "min_height kdt + 1 = height kdt"
using min_height_le_height[of kdt] assms(1) False
by (simp add: complete_iff_height balanced_def)
with * show ?thesis by arith
qed
subsection ‹Lemmas adapted from ‹HOL-Library.Tree_Real› to ‹k›-d Tree›
lemma size_height_log:
"log 2 (size_kdt kdt) ≤ height kdt"
by (simp add: log2_of_power_le size_height)
lemma min_height_size_log:
"min_height kdt ≤ log 2 (size_kdt kdt)"
by (simp add: le_log2_of_power min_height_size)
lemma size_log_if_complete:
"complete kdt ==> height kdt = log 2 (size_kdt kdt)"
using complete_iff_size log2_of_power_eq by blast
lemma min_height_size_log_if_incomplete:
"¬ complete kdt ==> min_height kdt < log 2 (size_kdt kdt)"
by (simp add: less_log2_of_power min_height_size_if_incomplete)
lemma min_height_balanced:
assumes "balanced kdt"
shows "min_height kdt = nat(floor(log 2 (size_kdt kdt)))"
proof cases
assume *: "complete kdt"
hence "size_kdt kdt = 2 ^ min_height kdt"
by (simp add: complete_iff_height size_if_complete)
from log2_of_power_eq[OF this] show ?thesis by linarith
next
assume *: "¬ complete kdt"
hence "height kdt = min_height kdt + 1"
using assms min_height_le_height[of kdt]
by(auto simp add: balanced_def complete_iff_height)
hence "size_kdt kdt < 2 ^ (min_height kdt + 1)"
by (metis * size_height_if_incomplete)
hence "log 2 (size_kdt kdt) < min_height kdt + 1"
using log2_of_power_less size_ge0 by blast
thus ?thesis using min_height_size_log[of kdt] by linarith
qed
lemma height_balanced:
assumes "balanced kdt"
shows "height kdt = nat(ceiling(log 2 (size_kdt kdt)))"
proof cases
assume *: "complete kdt"
hence "size_kdt kdt = 2 ^ height kdt"
by (simp add: size_if_complete)
from log2_of_power_eq[OF this] show ?thesis
by linarith
next
assume *: "¬ complete kdt"
hence **: "height kdt = min_height kdt + 1"
using assms min_height_le_height[of kdt]
by(auto simp add: balanced_def complete_iff_height)
hence "size_kdt kdt ≤ 2 ^ (min_height kdt + 1)" by (metis size_height)
from log2_of_power_le[OF this size_ge0] min_height_size_log_if_incomplete[OF *] **
show ?thesis by linarith
qed
lemma balanced_Node_if_wbal1:
assumes "balanced l" "balanced r" "size_kdt l = size_kdt r + 1"
shows "balanced (Node k v l r)"
proof -
from assms(3) have [simp]: "size_kdt l = size_kdt r + 1" by simp
have "nat ⌈log 2 (1 + size_kdt r)⌉ ≥ nat ⌈log 2 (size_kdt r)⌉"
by(rule nat_mono[OF ceiling_mono]) simp
hence 1: "height(Node k v l r) = nat ⌈log 2 (1 + size_kdt r)⌉ + 1"
using height_balanced[OF assms(1)] height_balanced[OF assms(2)]
by (simp del: nat_ceiling_le_eq add: max_def)
have "nat ⌊log 2 (1 + size_kdt r)⌋ ≥ nat ⌊log 2 (size_kdt r)⌋"
by(rule nat_mono[OF floor_mono]) simp
hence 2: "min_height(Node k v l r) = nat ⌊log 2 (size_kdt r)⌋ + 1"
using min_height_balanced[OF assms(1)] min_height_balanced[OF assms(2)]
by (simp)
have "size_kdt r ≥ 1" by (simp add: Suc_leI)
then obtain i where i: "2 ^ i ≤ size_kdt r" "size_kdt r < 2 ^ (i + 1)"
using ex_power_ivl1[of 2 "size_kdt r"] by auto
hence i1: "2 ^ i < size_kdt r + 1" "size_kdt r + 1 ≤ 2 ^ (i + 1)" by auto
from 1 2 floor_log_nat_eq_if[OF i] ceiling_log_nat_eq_if[OF i1]
show ?thesis by(simp add:balanced_def)
qed
lemma balanced_sym:
"balanced (Node k v l r) ==> balanced (Node k' v' r l)"
by (auto simp: balanced_def)
lemma balanced_Node_if_wbal2:
assumes "balanced l" "balanced r" "abs(int(size_kdt l) - int(size_kdt r)) ≤ 1"
shows "balanced (Node k v l r)"
proof -
have "size_kdt l = size_kdt r ∨ (size_kdt l = size_kdt r + 1 ∨ size_kdt r = size_kdt l + 1)" (is "?A ∨ ?B")
using assms(3) by linarith
thus ?thesis
proof
assume "?A"
thus ?thesis using assms(1,2)
apply(simp add: balanced_def min_def max_def)
by (metis assms(1,2) balanced_optimal le_antisym le_less)
next
assume "?B"
thus ?thesis
by (meson assms(1,2) balanced_sym balanced_Node_if_wbal1)
qed
qed
end
¤ Dauer der Verarbeitung: 0.12 Sekunden
¤
*© Formatika GbR, Deutschland