author | nipkow |
Mon, 10 Sep 2018 21:33:14 +0200 | |
changeset 68968 | 6c4421b006fb |
parent 68967 | cd32e6b34b5c |
child 68970 | b0dfe57dfa09 |
permissions | -rw-r--r-- |
66543 | 1 |
(* Author: Tobias Nipkow *) |
2 |
||
3 |
theory Sorting |
|
4 |
imports |
|
5 |
Complex_Main |
|
6 |
"HOL-Library.Multiset" |
|
7 |
begin |
|
8 |
||
68160 | 9 |
hide_const List.insort |
66543 | 10 |
|
11 |
declare Let_def [simp] |
|
12 |
||
13 |
||
14 |
subsection "Insertion Sort" |
|
15 |
||
16 |
fun insort :: "'a::linorder \<Rightarrow> 'a list \<Rightarrow> 'a list" where |
|
17 |
"insort x [] = [x]" | |
|
18 |
"insort x (y#ys) = |
|
19 |
(if x \<le> y then x#y#ys else y#(insort x ys))" |
|
20 |
||
21 |
fun isort :: "'a::linorder list \<Rightarrow> 'a list" where |
|
22 |
"isort [] = []" | |
|
23 |
"isort (x#xs) = insort x (isort xs)" |
|
24 |
||
68078 | 25 |
|
66543 | 26 |
subsubsection "Functional Correctness" |
27 |
||
28 |
lemma mset_insort: "mset (insort x xs) = add_mset x (mset xs)" |
|
29 |
apply(induction xs) |
|
30 |
apply auto |
|
31 |
done |
|
32 |
||
33 |
lemma mset_isort: "mset (isort xs) = mset xs" |
|
34 |
apply(induction xs) |
|
35 |
apply simp |
|
36 |
apply (simp add: mset_insort) |
|
37 |
done |
|
38 |
||
39 |
lemma set_insort: "set (insort x xs) = insert x (set xs)" |
|
40 |
by (metis mset_insort set_mset_add_mset_insert set_mset_mset) |
|
41 |
||
42 |
lemma sorted_insort: "sorted (insort a xs) = sorted xs" |
|
43 |
apply(induction xs) |
|
44 |
apply(auto simp add: set_insort) |
|
45 |
done |
|
46 |
||
68934 | 47 |
lemma sorted_isort: "sorted (isort xs)" |
66543 | 48 |
apply(induction xs) |
49 |
apply(auto simp: sorted_insort) |
|
50 |
done |
|
51 |
||
68078 | 52 |
|
53 |
subsubsection "Time Complexity" |
|
66543 | 54 |
|
55 |
text \<open>We count the number of function calls.\<close> |
|
56 |
||
57 |
text\<open> |
|
58 |
\<open>insort x [] = [x]\<close> |
|
59 |
\<open>insort x (y#ys) = |
|
60 |
(if x \<le> y then x#y#ys else y#(insort x ys))\<close> |
|
61 |
\<close> |
|
62 |
fun t_insort :: "'a::linorder \<Rightarrow> 'a list \<Rightarrow> nat" where |
|
63 |
"t_insort x [] = 1" | |
|
64 |
"t_insort x (y#ys) = |
|
65 |
(if x \<le> y then 0 else t_insort x ys) + 1" |
|
66 |
||
67 |
text\<open> |
|
68 |
\<open>isort [] = []\<close> |
|
69 |
\<open>isort (x#xs) = insort x (isort xs)\<close> |
|
70 |
\<close> |
|
71 |
fun t_isort :: "'a::linorder list \<Rightarrow> nat" where |
|
72 |
"t_isort [] = 1" | |
|
73 |
"t_isort (x#xs) = t_isort xs + t_insort x (isort xs) + 1" |
|
74 |
||
75 |
||
76 |
lemma t_insort_length: "t_insort x xs \<le> length xs + 1" |
|
77 |
apply(induction xs) |
|
78 |
apply auto |
|
79 |
done |
|
80 |
||
81 |
lemma length_insort: "length (insort x xs) = length xs + 1" |
|
82 |
apply(induction xs) |
|
83 |
apply auto |
|
84 |
done |
|
85 |
||
86 |
lemma length_isort: "length (isort xs) = length xs" |
|
87 |
apply(induction xs) |
|
88 |
apply (auto simp: length_insort) |
|
89 |
done |
|
90 |
||
91 |
lemma t_isort_length: "t_isort xs \<le> (length xs + 1) ^ 2" |
|
92 |
proof(induction xs) |
|
93 |
case Nil show ?case by simp |
|
94 |
next |
|
95 |
case (Cons x xs) |
|
96 |
have "t_isort (x#xs) = t_isort xs + t_insort x (isort xs) + 1" by simp |
|
97 |
also have "\<dots> \<le> (length xs + 1) ^ 2 + t_insort x (isort xs) + 1" |
|
98 |
using Cons.IH by simp |
|
99 |
also have "\<dots> \<le> (length xs + 1) ^ 2 + length xs + 1 + 1" |
|
100 |
using t_insort_length[of x "isort xs"] by (simp add: length_isort) |
|
101 |
also have "\<dots> \<le> (length(x#xs) + 1) ^ 2" |
|
102 |
by (simp add: power2_eq_square) |
|
103 |
finally show ?case . |
|
104 |
qed |
|
105 |
||
106 |
||
107 |
subsection "Merge Sort" |
|
108 |
||
109 |
fun merge :: "'a::linorder list \<Rightarrow> 'a list \<Rightarrow> 'a list" where |
|
110 |
"merge [] ys = ys" | |
|
111 |
"merge xs [] = xs" | |
|
112 |
"merge (x#xs) (y#ys) = (if x \<le> y then x # merge xs (y#ys) else y # merge (x#xs) ys)" |
|
113 |
||
114 |
fun msort :: "'a::linorder list \<Rightarrow> 'a list" where |
|
115 |
"msort xs = (let n = length xs in |
|
116 |
if n \<le> 1 then xs |
|
117 |
else merge (msort (take (n div 2) xs)) (msort (drop (n div 2) xs)))" |
|
118 |
||
119 |
declare msort.simps [simp del] |
|
120 |
||
68078 | 121 |
|
67983 | 122 |
subsubsection "Functional Correctness" |
123 |
||
124 |
lemma mset_merge: "mset(merge xs ys) = mset xs + mset ys" |
|
125 |
by(induction xs ys rule: merge.induct) auto |
|
126 |
||
68963 | 127 |
lemma mset_msort: "mset (msort xs) = mset xs" |
67983 | 128 |
proof(induction xs rule: msort.induct) |
129 |
case (1 xs) |
|
130 |
let ?n = "length xs" |
|
68966 | 131 |
let ?ys = "take (?n div 2) xs" |
132 |
let ?zs = "drop (?n div 2) xs" |
|
67983 | 133 |
show ?case |
134 |
proof cases |
|
135 |
assume "?n \<le> 1" |
|
136 |
thus ?thesis by(simp add: msort.simps[of xs]) |
|
137 |
next |
|
138 |
assume "\<not> ?n \<le> 1" |
|
68966 | 139 |
hence "mset (msort xs) = mset (msort ?ys) + mset (msort ?zs)" |
67983 | 140 |
by(simp add: msort.simps[of xs] mset_merge) |
68966 | 141 |
also have "\<dots> = mset ?ys + mset ?zs" |
67983 | 142 |
using \<open>\<not> ?n \<le> 1\<close> by(simp add: "1.IH") |
68966 | 143 |
also have "\<dots> = mset (?ys @ ?zs)" by (simp del: append_take_drop_id) |
67983 | 144 |
also have "\<dots> = mset xs" by simp |
145 |
finally show ?thesis . |
|
146 |
qed |
|
147 |
qed |
|
148 |
||
68966 | 149 |
text \<open>Via the previous lemma or directly:\<close> |
68963 | 150 |
|
67983 | 151 |
lemma set_merge: "set(merge xs ys) = set xs \<union> set ys" |
68963 | 152 |
by (metis mset_merge set_mset_mset set_mset_union) |
153 |
||
154 |
lemma "set(merge xs ys) = set xs \<union> set ys" |
|
67983 | 155 |
by(induction xs ys rule: merge.induct) (auto) |
156 |
||
157 |
lemma sorted_merge: "sorted (merge xs ys) \<longleftrightarrow> (sorted xs \<and> sorted ys)" |
|
158 |
by(induction xs ys rule: merge.induct) (auto simp: set_merge) |
|
159 |
||
68966 | 160 |
lemma sorted_msort: "sorted (msort xs)" |
67983 | 161 |
proof(induction xs rule: msort.induct) |
162 |
case (1 xs) |
|
163 |
let ?n = "length xs" |
|
164 |
show ?case |
|
165 |
proof cases |
|
166 |
assume "?n \<le> 1" |
|
167 |
thus ?thesis by(simp add: msort.simps[of xs] sorted01) |
|
168 |
next |
|
169 |
assume "\<not> ?n \<le> 1" |
|
170 |
thus ?thesis using "1.IH" |
|
68966 | 171 |
by(simp add: sorted_merge msort.simps[of xs]) |
67983 | 172 |
qed |
173 |
qed |
|
174 |
||
68078 | 175 |
|
176 |
subsubsection "Time Complexity" |
|
67983 | 177 |
|
178 |
text \<open>We only count the number of comparisons between list elements.\<close> |
|
66543 | 179 |
|
180 |
fun c_merge :: "'a::linorder list \<Rightarrow> 'a list \<Rightarrow> nat" where |
|
181 |
"c_merge [] ys = 0" | |
|
182 |
"c_merge xs [] = 0" | |
|
183 |
"c_merge (x#xs) (y#ys) = 1 + (if x \<le> y then c_merge xs (y#ys) else c_merge (x#xs) ys)" |
|
184 |
||
185 |
lemma c_merge_ub: "c_merge xs ys \<le> length xs + length ys" |
|
186 |
by (induction xs ys rule: c_merge.induct) auto |
|
187 |
||
188 |
fun c_msort :: "'a::linorder list \<Rightarrow> nat" where |
|
189 |
"c_msort xs = |
|
190 |
(let n = length xs; |
|
191 |
ys = take (n div 2) xs; |
|
192 |
zs = drop (n div 2) xs |
|
193 |
in if n \<le> 1 then 0 |
|
194 |
else c_msort ys + c_msort zs + c_merge (msort ys) (msort zs))" |
|
195 |
||
196 |
declare c_msort.simps [simp del] |
|
197 |
||
198 |
lemma length_merge: "length(merge xs ys) = length xs + length ys" |
|
199 |
apply (induction xs ys rule: merge.induct) |
|
200 |
apply auto |
|
201 |
done |
|
202 |
||
203 |
lemma length_msort: "length(msort xs) = length xs" |
|
204 |
proof (induction xs rule: msort.induct) |
|
205 |
case (1 xs) |
|
206 |
thus ?case by (auto simp: msort.simps[of xs] length_merge) |
|
207 |
qed |
|
208 |
text \<open>Why structured proof? |
|
209 |
To have the name "xs" to specialize msort.simps with xs |
|
210 |
to ensure that msort.simps cannot be used recursively. |
|
211 |
Also works without this precaution, but that is just luck.\<close> |
|
212 |
||
213 |
lemma c_msort_le: "length xs = 2^k \<Longrightarrow> c_msort xs \<le> k * 2^k" |
|
214 |
proof(induction k arbitrary: xs) |
|
215 |
case 0 thus ?case by (simp add: c_msort.simps) |
|
216 |
next |
|
217 |
case (Suc k) |
|
218 |
let ?n = "length xs" |
|
219 |
let ?ys = "take (?n div 2) xs" |
|
220 |
let ?zs = "drop (?n div 2) xs" |
|
221 |
show ?case |
|
222 |
proof (cases "?n \<le> 1") |
|
223 |
case True |
|
224 |
thus ?thesis by(simp add: c_msort.simps) |
|
225 |
next |
|
226 |
case False |
|
227 |
have "c_msort(xs) = |
|
228 |
c_msort ?ys + c_msort ?zs + c_merge (msort ?ys) (msort ?zs)" |
|
229 |
by (simp add: c_msort.simps msort.simps) |
|
230 |
also have "\<dots> \<le> c_msort ?ys + c_msort ?zs + length ?ys + length ?zs" |
|
231 |
using c_merge_ub[of "msort ?ys" "msort ?zs"] length_msort[of ?ys] length_msort[of ?zs] |
|
232 |
by arith |
|
233 |
also have "\<dots> \<le> k * 2^k + c_msort ?zs + length ?ys + length ?zs" |
|
234 |
using Suc.IH[of ?ys] Suc.prems by simp |
|
235 |
also have "\<dots> \<le> k * 2^k + k * 2^k + length ?ys + length ?zs" |
|
236 |
using Suc.IH[of ?zs] Suc.prems by simp |
|
237 |
also have "\<dots> = 2 * k * 2^k + 2 * 2 ^ k" |
|
238 |
using Suc.prems by simp |
|
239 |
finally show ?thesis by simp |
|
240 |
qed |
|
241 |
qed |
|
242 |
||
243 |
(* Beware of conversions: *) |
|
68963 | 244 |
lemma c_msort_log: "length xs = 2^k \<Longrightarrow> c_msort xs \<le> length xs * log 2 (length xs)" |
66543 | 245 |
using c_msort_le[of xs k] apply (simp add: log_nat_power algebra_simps) |
66912
a99a7cbf0fb5
generalized lemmas cancelling real_of_int/real in (in)equalities with power; completed set of related simp rules; lemmas about floorlog/bitlen
immler
parents:
66543
diff
changeset
|
246 |
by (metis (mono_tags) numeral_power_eq_of_nat_cancel_iff of_nat_le_iff of_nat_mult) |
66543 | 247 |
|
67983 | 248 |
|
249 |
subsection "Bottom-Up Merge Sort" |
|
250 |
||
251 |
(* Exercise: make tail recursive *) |
|
252 |
fun merge_adj :: "('a::linorder) list list \<Rightarrow> 'a list list" where |
|
253 |
"merge_adj [] = []" | |
|
254 |
"merge_adj [xs] = [xs]" | |
|
255 |
"merge_adj (xs # ys # zss) = merge xs ys # merge_adj zss" |
|
256 |
||
257 |
text \<open>For the termination proof of \<open>merge_all\<close> below.\<close> |
|
258 |
lemma length_merge_adjacent[simp]: "length (merge_adj xs) = (length xs + 1) div 2" |
|
259 |
by (induction xs rule: merge_adj.induct) auto |
|
260 |
||
261 |
fun merge_all :: "('a::linorder) list list \<Rightarrow> 'a list" where |
|
262 |
"merge_all [] = undefined" | |
|
263 |
"merge_all [xs] = xs" | |
|
264 |
"merge_all xss = merge_all (merge_adj xss)" |
|
265 |
||
266 |
definition msort_bu :: "('a::linorder) list \<Rightarrow> 'a list" where |
|
267 |
"msort_bu xs = (if xs = [] then [] else merge_all (map (\<lambda>x. [x]) xs))" |
|
268 |
||
68078 | 269 |
|
67983 | 270 |
subsubsection "Functional Correctness" |
271 |
||
272 |
lemma mset_merge_adj: |
|
273 |
"\<Union># image_mset mset (mset (merge_adj xss)) = \<Union># image_mset mset (mset xss)" |
|
274 |
by(induction xss rule: merge_adj.induct) (auto simp: mset_merge) |
|
275 |
||
68967 | 276 |
lemma mset_merge_all: |
67983 | 277 |
"xss \<noteq> [] \<Longrightarrow> mset (merge_all xss) = (\<Union># (mset (map mset xss)))" |
278 |
by(induction xss rule: merge_all.induct) (auto simp: mset_merge mset_merge_adj) |
|
279 |
||
68968 | 280 |
lemma mset_msort_bu: "mset (msort_bu xs) = mset xs" |
281 |
by(simp add: msort_bu_def mset_merge_all comp_def) |
|
282 |
||
67983 | 283 |
lemma sorted_merge_adj: |
284 |
"\<forall>xs \<in> set xss. sorted xs \<Longrightarrow> \<forall>xs \<in> set (merge_adj xss). sorted xs" |
|
285 |
by(induction xss rule: merge_adj.induct) (auto simp: sorted_merge) |
|
286 |
||
287 |
lemma sorted_merge_all: |
|
288 |
"\<forall>xs \<in> set xss. sorted xs \<Longrightarrow> xss \<noteq> [] \<Longrightarrow> sorted (merge_all xss)" |
|
289 |
apply(induction xss rule: merge_all.induct) |
|
290 |
using [[simp_depth_limit=3]] by (auto simp add: sorted_merge_adj) |
|
291 |
||
292 |
lemma sorted_msort_bu: "sorted (msort_bu xs)" |
|
293 |
by(simp add: msort_bu_def sorted_merge_all) |
|
294 |
||
68078 | 295 |
|
296 |
subsubsection "Time Complexity" |
|
67983 | 297 |
|
68139 | 298 |
fun c_merge_adj :: "('a::linorder) list list \<Rightarrow> nat" where |
67983 | 299 |
"c_merge_adj [] = 0" | |
68161 | 300 |
"c_merge_adj [xs] = 0" | |
301 |
"c_merge_adj (xs # ys # zss) = c_merge xs ys + c_merge_adj zss" |
|
67983 | 302 |
|
68139 | 303 |
fun c_merge_all :: "('a::linorder) list list \<Rightarrow> nat" where |
68079 | 304 |
"c_merge_all [] = undefined" | |
68161 | 305 |
"c_merge_all [xs] = 0" | |
306 |
"c_merge_all xss = c_merge_adj xss + c_merge_all (merge_adj xss)" |
|
67983 | 307 |
|
68139 | 308 |
definition c_msort_bu :: "('a::linorder) list \<Rightarrow> nat" where |
67983 | 309 |
"c_msort_bu xs = (if xs = [] then 0 else c_merge_all (map (\<lambda>x. [x]) xs))" |
310 |
||
311 |
lemma length_merge_adj: |
|
68161 | 312 |
"\<lbrakk> even(length xss); \<forall>x \<in> set xss. length x = m \<rbrakk> \<Longrightarrow> \<forall>xs \<in> set (merge_adj xss). length xs = 2*m" |
313 |
by(induction xss rule: merge_adj.induct) (auto simp: length_merge) |
|
67983 | 314 |
|
68161 | 315 |
lemma c_merge_adj: "\<forall>xs \<in> set xss. length xs = m \<Longrightarrow> c_merge_adj xss \<le> m * length xss" |
316 |
proof(induction xss rule: c_merge_adj.induct) |
|
67983 | 317 |
case 1 thus ?case by simp |
318 |
next |
|
319 |
case 2 thus ?case by simp |
|
320 |
next |
|
321 |
case (3 x y) thus ?case using c_merge_ub[of x y] by (simp add: algebra_simps) |
|
322 |
qed |
|
323 |
||
68161 | 324 |
lemma c_merge_all: "\<lbrakk> \<forall>xs \<in> set xss. length xs = m; length xss = 2^k \<rbrakk> |
325 |
\<Longrightarrow> c_merge_all xss \<le> m * k * 2^k" |
|
326 |
proof (induction xss arbitrary: k m rule: c_merge_all.induct) |
|
67983 | 327 |
case 1 thus ?case by simp |
328 |
next |
|
68158 | 329 |
case 2 thus ?case by simp |
67983 | 330 |
next |
68162 | 331 |
case (3 xs ys xss) |
332 |
let ?xss = "xs # ys # xss" |
|
333 |
let ?xss2 = "merge_adj ?xss" |
|
67983 | 334 |
obtain k' where k': "k = Suc k'" using "3.prems"(2) |
335 |
by (metis length_Cons nat.inject nat_power_eq_Suc_0_iff nat.exhaust) |
|
68162 | 336 |
have "even (length xss)" using "3.prems"(2) even_Suc_Suc_iff by fastforce |
67983 | 337 |
from "3.prems"(1) length_merge_adj[OF this] |
68162 | 338 |
have *: "\<forall>x \<in> set(merge_adj ?xss). length x = 2*m" by(auto simp: length_merge) |
339 |
have **: "length ?xss2 = 2 ^ k'" using "3.prems"(2) k' by auto |
|
340 |
have "c_merge_all ?xss = c_merge_adj ?xss + c_merge_all ?xss2" by simp |
|
341 |
also have "\<dots> \<le> m * 2^k + c_merge_all ?xss2" |
|
67983 | 342 |
using "3.prems"(2) c_merge_adj[OF "3.prems"(1)] by (auto simp: algebra_simps) |
343 |
also have "\<dots> \<le> m * 2^k + (2*m) * k' * 2^k'" |
|
68079 | 344 |
using "3.IH"[OF * **] by simp |
67983 | 345 |
also have "\<dots> = m * k * 2^k" |
346 |
using k' by (simp add: algebra_simps) |
|
347 |
finally show ?case . |
|
348 |
qed |
|
349 |
||
350 |
corollary c_msort_bu: "length xs = 2 ^ k \<Longrightarrow> c_msort_bu xs \<le> k * 2 ^ k" |
|
351 |
using c_merge_all[of "map (\<lambda>x. [x]) xs" 1] by (simp add: c_msort_bu_def) |
|
352 |
||
66543 | 353 |
end |