4 |
4 |
5 theory Sorting_Algorithms |
5 theory Sorting_Algorithms |
6 imports Main Multiset Comparator |
6 imports Main Multiset Comparator |
7 begin |
7 begin |
8 |
8 |
9 text \<open>Prelude\<close> |
|
10 |
|
11 hide_const (open) sorted insort sort |
|
12 |
|
13 |
|
14 section \<open>Stably sorted lists\<close> |
9 section \<open>Stably sorted lists\<close> |
15 |
10 |
16 abbreviation (input) stable_segment :: "'a comparator \<Rightarrow> 'a \<Rightarrow> 'a list \<Rightarrow> 'a list" |
11 abbreviation (input) stable_segment :: "'a comparator \<Rightarrow> 'a \<Rightarrow> 'a list \<Rightarrow> 'a list" |
17 where "stable_segment cmp x \<equiv> filter (\<lambda>y. compare cmp x y = Equiv)" |
12 where "stable_segment cmp x \<equiv> filter (\<lambda>y. compare cmp x y = Equiv)" |
18 |
13 |
19 fun sorted :: "'a comparator \<Rightarrow> 'a list \<Rightarrow> bool" |
14 fun sorted :: "'a comparator \<Rightarrow> 'a list \<Rightarrow> bool" |
20 where sorted_Nil: "sorted cmp [] \<longleftrightarrow> True" |
15 where sorted_Nil: "sorted cmp [] \<longleftrightarrow> True" |
21 | sorted_single: "sorted cmp [x] \<longleftrightarrow> True" |
16 | sorted_single: "sorted cmp [x] \<longleftrightarrow> True" |
22 | sorted_rec: "sorted cmp (y # x # xs) \<longleftrightarrow> compare cmp x y \<noteq> Less \<and> sorted cmp (x # xs)" |
17 | sorted_rec: "sorted cmp (y # x # xs) \<longleftrightarrow> compare cmp y x \<noteq> Greater \<and> sorted cmp (x # xs)" |
23 |
18 |
24 lemma sorted_ConsI: |
19 lemma sorted_ConsI: |
25 "sorted cmp (x # xs)" if "sorted cmp xs" |
20 "sorted cmp (x # xs)" if "sorted cmp xs" |
26 and "\<And>y ys. xs = y # ys \<Longrightarrow> compare cmp y x \<noteq> Less" |
21 and "\<And>y ys. xs = y # ys \<Longrightarrow> compare cmp x y \<noteq> Greater" |
27 using that by (cases xs) simp_all |
22 using that by (cases xs) simp_all |
|
23 |
|
24 lemma sorted_Cons_imp_sorted: |
|
25 "sorted cmp xs" if "sorted cmp (x # xs)" |
|
26 using that by (cases xs) simp_all |
|
27 |
|
28 lemma sorted_Cons_imp_not_less: |
|
29 "compare cmp y x \<noteq> Greater" if "sorted cmp (y # xs)" |
|
30 and "x \<in> set xs" |
|
31 using that by (induction xs arbitrary: y) (auto dest: compare.trans_not_greater) |
28 |
32 |
29 lemma sorted_induct [consumes 1, case_names Nil Cons, induct pred: sorted]: |
33 lemma sorted_induct [consumes 1, case_names Nil Cons, induct pred: sorted]: |
30 "P xs" if "sorted cmp xs" and "P []" |
34 "P xs" if "sorted cmp xs" and "P []" |
31 and *: "\<And>x xs. sorted cmp xs \<Longrightarrow> P xs |
35 and *: "\<And>x xs. sorted cmp xs \<Longrightarrow> P xs |
32 \<Longrightarrow> (\<And>y. y \<in> set xs \<Longrightarrow> compare cmp y x \<noteq> Less) \<Longrightarrow> P (x # xs)" |
36 \<Longrightarrow> (\<And>y. y \<in> set xs \<Longrightarrow> compare cmp x y \<noteq> Greater) \<Longrightarrow> P (x # xs)" |
33 using \<open>sorted cmp xs\<close> proof (induction xs) |
37 using \<open>sorted cmp xs\<close> proof (induction xs) |
34 case Nil |
38 case Nil |
35 show ?case |
39 show ?case |
36 by (rule \<open>P []\<close>) |
40 by (rule \<open>P []\<close>) |
37 next |
41 next |
38 case (Cons x xs) |
42 case (Cons x xs) |
39 from \<open>sorted cmp (x # xs)\<close> have "sorted cmp xs" |
43 from \<open>sorted cmp (x # xs)\<close> have "sorted cmp xs" |
40 by (cases xs) simp_all |
44 by (cases xs) simp_all |
41 moreover have "P xs" using \<open>sorted cmp xs\<close> |
45 moreover have "P xs" using \<open>sorted cmp xs\<close> |
42 by (rule Cons.IH) |
46 by (rule Cons.IH) |
43 moreover have "compare cmp y x \<noteq> Less" if "y \<in> set xs" for y |
47 moreover have "compare cmp x y \<noteq> Greater" if "y \<in> set xs" for y |
44 using that \<open>sorted cmp (x # xs)\<close> proof (induction xs) |
48 using that \<open>sorted cmp (x # xs)\<close> proof (induction xs) |
45 case Nil |
49 case Nil |
46 then show ?case |
50 then show ?case |
47 by simp |
51 by simp |
48 next |
52 next |
67 qed |
71 qed |
68 |
72 |
69 lemma sorted_induct_remove1 [consumes 1, case_names Nil minimum]: |
73 lemma sorted_induct_remove1 [consumes 1, case_names Nil minimum]: |
70 "P xs" if "sorted cmp xs" and "P []" |
74 "P xs" if "sorted cmp xs" and "P []" |
71 and *: "\<And>x xs. sorted cmp xs \<Longrightarrow> P (remove1 x xs) |
75 and *: "\<And>x xs. sorted cmp xs \<Longrightarrow> P (remove1 x xs) |
72 \<Longrightarrow> x \<in> set xs \<Longrightarrow> hd (stable_segment cmp x xs) = x \<Longrightarrow> (\<And>y. y \<in> set xs \<Longrightarrow> compare cmp y x \<noteq> Less) |
76 \<Longrightarrow> x \<in> set xs \<Longrightarrow> hd (stable_segment cmp x xs) = x \<Longrightarrow> (\<And>y. y \<in> set xs \<Longrightarrow> compare cmp x y \<noteq> Greater) |
73 \<Longrightarrow> P xs" |
77 \<Longrightarrow> P xs" |
74 using \<open>sorted cmp xs\<close> proof (induction xs) |
78 using \<open>sorted cmp xs\<close> proof (induction xs) |
75 case Nil |
79 case Nil |
76 show ?case |
80 show ?case |
77 by (rule \<open>P []\<close>) |
81 by (rule \<open>P []\<close>) |
78 next |
82 next |
79 case (Cons x xs) |
83 case (Cons x xs) |
80 then have "sorted cmp (x # xs)" |
84 then have "sorted cmp (x # xs)" |
81 by (simp add: sorted_ConsI) |
85 by (simp add: sorted_ConsI) |
82 moreover note Cons.IH |
86 moreover note Cons.IH |
83 moreover have "\<And>y. compare cmp y x = Less \<Longrightarrow> y \<in> set xs \<Longrightarrow> False" |
87 moreover have "\<And>y. compare cmp x y = Greater \<Longrightarrow> y \<in> set xs \<Longrightarrow> False" |
84 using Cons.hyps by simp |
88 using Cons.hyps by simp |
85 ultimately show ?case |
89 ultimately show ?case |
86 by (auto intro!: * [of "x # xs" x]) blast |
90 by (auto intro!: * [of "x # xs" x]) blast |
87 qed |
91 qed |
88 |
92 |
112 proof (rule sorted_ConsI) |
116 proof (rule sorted_ConsI) |
113 fix z zs |
117 fix z zs |
114 assume "remove1 x ys = z # zs" |
118 assume "remove1 x ys = z # zs" |
115 with \<open>x \<noteq> y\<close> have "z \<in> set ys" |
119 with \<open>x \<noteq> y\<close> have "z \<in> set ys" |
116 using notin_set_remove1 [of z ys x] by auto |
120 using notin_set_remove1 [of z ys x] by auto |
117 then show "compare cmp z y \<noteq> Less" |
121 then show "compare cmp y z \<noteq> Greater" |
118 by (rule Cons.hyps(2)) |
122 by (rule Cons.hyps(2)) |
119 qed |
123 qed |
120 with False show ?thesis |
124 with False show ?thesis |
121 by simp |
125 by simp |
122 qed |
126 qed |
123 qed |
127 qed |
124 qed |
128 qed |
125 |
129 |
|
130 lemma sorted_stable_segment: |
|
131 "sorted cmp (stable_segment cmp x xs)" |
|
132 proof (induction xs) |
|
133 case Nil |
|
134 show ?case |
|
135 by simp |
|
136 next |
|
137 case (Cons y ys) |
|
138 then show ?case |
|
139 by (auto intro!: sorted_ConsI simp add: filter_eq_Cons_iff compare.sym) |
|
140 (auto dest: compare.trans_equiv simp add: compare.sym compare.greater_iff_sym_less) |
|
141 |
|
142 qed |
|
143 |
126 primrec insort :: "'a comparator \<Rightarrow> 'a \<Rightarrow> 'a list \<Rightarrow> 'a list" |
144 primrec insort :: "'a comparator \<Rightarrow> 'a \<Rightarrow> 'a list \<Rightarrow> 'a list" |
127 where "insort cmp y [] = [y]" |
145 where "insort cmp y [] = [y]" |
128 | "insort cmp y (x # xs) = (if compare cmp y x \<noteq> Greater |
146 | "insort cmp y (x # xs) = (if compare cmp y x \<noteq> Greater |
129 then y # x # xs |
147 then y # x # xs |
130 else x # insort cmp y xs)" |
148 else x # insort cmp y xs)" |
173 "remove1 x (insort cmp x xs) = xs" |
191 "remove1 x (insort cmp x xs) = xs" |
174 by (induction xs) simp_all |
192 by (induction xs) simp_all |
175 |
193 |
176 lemma insort_eq_ConsI: |
194 lemma insort_eq_ConsI: |
177 "insort cmp x xs = x # xs" |
195 "insort cmp x xs = x # xs" |
178 if "sorted cmp xs" "\<And>y. y \<in> set xs \<Longrightarrow> compare cmp y x \<noteq> Less" |
196 if "sorted cmp xs" "\<And>y. y \<in> set xs \<Longrightarrow> compare cmp x y \<noteq> Greater" |
179 using that by (induction xs) (simp_all add: compare.greater_iff_sym_less) |
197 using that by (induction xs) (simp_all add: compare.greater_iff_sym_less) |
180 |
198 |
181 lemma remove1_insort_not_same_eq [simp]: |
199 lemma remove1_insort_not_same_eq [simp]: |
182 "remove1 y (insort cmp x xs) = insort cmp x (remove1 y xs)" |
200 "remove1 y (insort cmp x xs) = insort cmp x (remove1 y xs)" |
183 if "sorted cmp xs" "x \<noteq> y" |
201 if "sorted cmp xs" "x \<noteq> y" |
223 with Cons.prems have "x = y" |
242 with Cons.prems have "x = y" |
224 by simp |
243 by simp |
225 with Cons.hyps show ?thesis |
244 with Cons.hyps show ?thesis |
226 by (simp add: insort_eq_ConsI) |
245 by (simp add: insort_eq_ConsI) |
227 qed |
246 qed |
|
247 qed |
|
248 |
|
249 lemma sorted_append_iff: |
|
250 "sorted cmp (xs @ ys) \<longleftrightarrow> sorted cmp xs \<and> sorted cmp ys |
|
251 \<and> (\<forall>x \<in> set xs. \<forall>y \<in> set ys. compare cmp x y \<noteq> Greater)" (is "?P \<longleftrightarrow> ?R \<and> ?S \<and> ?Q") |
|
252 proof |
|
253 assume ?P |
|
254 have ?R |
|
255 using \<open>?P\<close> by (induction xs) |
|
256 (auto simp add: sorted_Cons_imp_not_less, |
|
257 auto simp add: sorted_Cons_imp_sorted intro: sorted_ConsI) |
|
258 moreover have ?S |
|
259 using \<open>?P\<close> by (induction xs) (auto dest: sorted_Cons_imp_sorted) |
|
260 moreover have ?Q |
|
261 using \<open>?P\<close> by (induction xs) (auto simp add: sorted_Cons_imp_not_less, |
|
262 simp add: sorted_Cons_imp_sorted) |
|
263 ultimately show "?R \<and> ?S \<and> ?Q" |
|
264 by simp |
|
265 next |
|
266 assume "?R \<and> ?S \<and> ?Q" |
|
267 then have ?R ?S ?Q |
|
268 by simp_all |
|
269 then show ?P |
|
270 by (induction xs) |
|
271 (auto simp add: append_eq_Cons_conv intro!: sorted_ConsI) |
228 qed |
272 qed |
229 |
273 |
230 definition sort :: "'a comparator \<Rightarrow> 'a list \<Rightarrow> 'a list" |
274 definition sort :: "'a comparator \<Rightarrow> 'a list \<Rightarrow> 'a list" |
231 where "sort cmp xs = foldr (insort cmp) xs []" |
275 where "sort cmp xs = foldr (insort cmp) xs []" |
232 |
276 |
316 by (simp add: insort_remove1_same_eq) |
360 by (simp add: insort_remove1_same_eq) |
317 finally show ?case . |
361 finally show ?case . |
318 qed |
362 qed |
319 qed |
363 qed |
320 |
364 |
|
365 lemma filter_insort: |
|
366 "filter P (insort cmp x xs) = insort cmp x (filter P xs)" |
|
367 if "sorted cmp xs" and "P x" |
|
368 using that by (induction xs) |
|
369 (auto simp add: compare.trans_not_greater insort_eq_ConsI) |
|
370 |
|
371 lemma filter_insort_triv: |
|
372 "filter P (insort cmp x xs) = filter P xs" |
|
373 if "\<not> P x" |
|
374 using that by (induction xs) simp_all |
|
375 |
|
376 lemma filter_sort: |
|
377 "filter P (sort cmp xs) = sort cmp (filter P xs)" |
|
378 by (induction xs) (auto simp add: filter_insort filter_insort_triv) |
|
379 |
|
380 |
|
381 section \<open>Alternative sorting algorithms\<close> |
|
382 |
|
383 subsection \<open>Quicksort\<close> |
|
384 |
|
385 definition quicksort :: "'a comparator \<Rightarrow> 'a list \<Rightarrow> 'a list" |
|
386 where quicksort_is_sort [simp]: "quicksort = sort" |
|
387 |
|
388 lemma sort_by_quicksort: |
|
389 "sort = quicksort" |
|
390 by simp |
|
391 |
|
392 lemma sort_by_quicksort_rec: |
|
393 "sort cmp xs = sort cmp [x\<leftarrow>xs. compare cmp x (xs ! (length xs div 2)) = Less] |
|
394 @ stable_segment cmp (xs ! (length xs div 2)) xs |
|
395 @ sort cmp [x\<leftarrow>xs. compare cmp x (xs ! (length xs div 2)) = Greater]" (is "sort cmp ?lhs = ?rhs") |
|
396 proof (rule sort_eqI) |
|
397 show "mset ?lhs = mset ?rhs" |
|
398 by (rule multiset_eqI) (auto simp add: compare.sym intro: comp.exhaust) |
|
399 next |
|
400 show "sorted cmp ?rhs" |
|
401 by (auto simp add: sorted_append_iff sorted_stable_segment compare.equiv_subst_right dest: compare.trans_greater) |
|
402 next |
|
403 let ?pivot = "xs ! (length xs div 2)" |
|
404 fix l |
|
405 assume "l \<in> set xs" |
|
406 have "compare cmp x ?pivot = comp \<and> compare cmp l x = Equiv |
|
407 \<longleftrightarrow> compare cmp l ?pivot = comp \<and> compare cmp l x = Equiv" for x comp |
|
408 proof - |
|
409 have "compare cmp x ?pivot = comp \<longleftrightarrow> compare cmp l ?pivot = comp" |
|
410 if "compare cmp l x = Equiv" |
|
411 using that by (simp add: compare.equiv_subst_left compare.sym) |
|
412 then show ?thesis by blast |
|
413 qed |
|
414 then show "stable_segment cmp l ?lhs = stable_segment cmp l ?rhs" |
|
415 by (simp add: stable_sort compare.sym [of _ ?pivot]) |
|
416 (cases "compare cmp l ?pivot", simp_all) |
|
417 qed |
|
418 |
|
419 context |
|
420 begin |
|
421 |
|
422 qualified definition partition :: "'a comparator \<Rightarrow> 'a \<Rightarrow> 'a list \<Rightarrow> 'a list \<times> 'a list \<times> 'a list" |
|
423 where "partition cmp pivot xs = |
|
424 ([x \<leftarrow> xs. compare cmp x pivot = Less], stable_segment cmp pivot xs, [x \<leftarrow> xs. compare cmp x pivot = Greater])" |
|
425 |
|
426 qualified lemma partition_code [code]: |
|
427 "partition cmp pivot [] = ([], [], [])" |
|
428 "partition cmp pivot (x # xs) = |
|
429 (let (lts, eqs, gts) = partition cmp pivot xs |
|
430 in case compare cmp x pivot of |
|
431 Less \<Rightarrow> (x # lts, eqs, gts) |
|
432 | Equiv \<Rightarrow> (lts, x # eqs, gts) |
|
433 | Greater \<Rightarrow> (lts, eqs, x # gts))" |
|
434 using comp.exhaust by (auto simp add: partition_def Let_def compare.sym [of _ pivot]) |
|
435 |
|
436 lemma quicksort_code [code]: |
|
437 "quicksort cmp xs = |
|
438 (case xs of |
|
439 [] \<Rightarrow> [] |
|
440 | [x] \<Rightarrow> xs |
|
441 | [x, y] \<Rightarrow> (if compare cmp x y \<noteq> Greater then xs else [y, x]) |
|
442 | _ \<Rightarrow> |
|
443 let (lts, eqs, gts) = partition cmp (xs ! (length xs div 2)) xs |
|
444 in quicksort cmp lts @ eqs @ quicksort cmp gts)" |
|
445 proof (cases "length xs \<ge> 3") |
|
446 case False |
|
447 then have "length xs \<le> 2" |
|
448 by simp |
|
449 then have "length xs = 0 \<or> length xs = 1 \<or> length xs = 2" |
|
450 using le_neq_trans less_2_cases by auto |
|
451 then consider "xs = []" | x where "xs = [x]" | x y where "xs = [x, y]" |
|
452 by (auto simp add: length_Suc_conv numeral_2_eq_2) |
|
453 then show ?thesis |
|
454 by cases simp_all |
|
455 next |
|
456 case True |
|
457 then obtain x y z zs where "xs = x # y # z # zs" |
|
458 by (metis le_0_eq length_0_conv length_Cons list.exhaust not_less_eq_eq numeral_3_eq_3) |
|
459 moreover have "quicksort cmp xs = |
|
460 (let (lts, eqs, gts) = partition cmp (xs ! (length xs div 2)) xs |
|
461 in quicksort cmp lts @ eqs @ quicksort cmp gts)" |
|
462 using sort_by_quicksort_rec [of cmp xs] by (simp add: partition_def) |
|
463 ultimately show ?thesis |
|
464 by simp |
|
465 qed |
|
466 |
321 end |
467 end |
|
468 |
|
469 text \<open>Evaluation example\<close> |
|
470 |
|
471 value "let cmp = key abs (reversed default) |
|
472 in quicksort cmp [65, 1705, -2322, 734, 4, (-17::int)]" |
|
473 |
|
474 end |