src/HOL/Library/Sorting_Algorithms.thy
changeset 69246 c1fe9dcc274a
parent 69194 6d514e128a85
child 69250 1011f0b46af7
equal deleted inserted replaced
69245:3e9f812c308c 69246:c1fe9dcc274a
     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
    52       case Nil
    56       case Nil
    53       with Cons.prems show ?thesis
    57       with Cons.prems show ?thesis
    54         by simp
    58         by simp
    55     next
    59     next
    56       case (Cons w ws)
    60       case (Cons w ws)
    57       with Cons.prems have "compare cmp w z \<noteq> Less" "compare cmp z x \<noteq> Less"
    61       with Cons.prems have "compare cmp z w \<noteq> Greater" "compare cmp x z \<noteq> Greater"
    58         by auto
    62         by auto
    59       then have "compare cmp w x \<noteq> Less"
    63       then have "compare cmp x w \<noteq> Greater"
    60         by (auto dest: compare.trans_not_less)
    64         by (auto dest: compare.trans_not_greater)
    61       with Cons show ?thesis
    65       with Cons show ?thesis
    62         using Cons.prems Cons.IH by auto
    66         using Cons.prems Cons.IH by auto
    63     qed
    67     qed
    64   qed
    68   qed
    65   ultimately show ?case
    69   ultimately show ?case
    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"
   186   then show ?case
   204   then show ?case
   187     by simp
   205     by simp
   188 next
   206 next
   189   case (Cons z zs)
   207   case (Cons z zs)
   190   show ?case
   208   show ?case
   191   proof (cases "compare cmp z x = Less")
   209   proof (cases "compare cmp x z = Greater")
   192     case True
   210     case True
   193     with Cons show ?thesis
   211     with Cons show ?thesis
   194       by (simp add: compare.greater_iff_sym_less)
   212       by simp
   195   next
   213   next
   196     case False
   214     case False
   197     have "compare cmp y x \<noteq> Less" if "y \<in> set zs" for y
   215     then have "compare cmp x y \<noteq> Greater" if "y \<in> set zs" for y
   198       using that False Cons.hyps by (auto dest: compare.trans_not_less)
   216       using that Cons.hyps
       
   217       by (auto dest: compare.trans_not_greater)
   199     with Cons show ?thesis
   218     with Cons show ?thesis
   200       by (simp add: insort_eq_ConsI)
   219       by (simp add: insort_eq_ConsI)
   201   qed
   220   qed
   202 qed
   221 qed
   203 
   222 
   209   then show ?case
   228   then show ?case
   210     by simp
   229     by simp
   211 next
   230 next
   212   case (Cons y ys)
   231   case (Cons y ys)
   213   then have "compare cmp x y \<noteq> Less"
   232   then have "compare cmp x y \<noteq> Less"
   214     by auto
   233     by (auto simp add: compare.greater_iff_sym_less)
   215   then consider "compare cmp x y = Greater" | "compare cmp x y = Equiv"
   234   then consider "compare cmp x y = Greater" | "compare cmp x y = Equiv"
   216     by (cases "compare cmp x y") auto
   235     by (cases "compare cmp x y") auto
   217   then show ?case proof cases
   236   then show ?case proof cases
   218     case 1
   237     case 1
   219     with Cons.prems Cons.IH show ?thesis
   238     with Cons.prems Cons.IH show ?thesis
   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 
   297       by simp
   341       by simp
   298   next
   342   next
   299     case (minimum x xs)
   343     case (minimum x xs)
   300     from \<open>mset ys = mset xs\<close> have ys: "set ys = set xs"
   344     from \<open>mset ys = mset xs\<close> have ys: "set ys = set xs"
   301       by (rule mset_eq_setD)
   345       by (rule mset_eq_setD)
   302     then have "compare cmp y x \<noteq> Less" if "y \<in> set ys" for y
   346     then have "compare cmp x y \<noteq> Greater" if "y \<in> set ys" for y
   303       using that minimum.hyps by simp
   347       using that minimum.hyps by simp
   304     from minimum.prems have stable: "stable_segment cmp x ys = stable_segment cmp x xs"
   348     from minimum.prems have stable: "stable_segment cmp x ys = stable_segment cmp x xs"
   305       by simp
   349       by simp
   306     have "sort cmp (remove1 x ys) = remove1 x xs"
   350     have "sort cmp (remove1 x ys) = remove1 x xs"
   307       by (rule minimum.IH) (simp_all add: minimum.prems filter_remove1)
   351       by (rule minimum.IH) (simp_all add: minimum.prems filter_remove1)
   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