SortInsertion Sort
Overview
The Insertion-Sort Program
- to insert an element i into an already sorted list l, simply
walk down the list until we find an item greater than or equal
to i---then put i into the list; and
- given an unsorted list, insert each element into the recursive result of sorting the list.
Fixpoint insert (i:nat) (l: list nat) :=
match l with
| nil ⇒ i::nil
| h::t ⇒ if leb i h then i::h::t else h :: insert i t
end.
Fixpoint sort (l: list nat) : list nat :=
match l with
| nil ⇒ nil
| h::t ⇒ insert h (sort t)
end.
Does our sorting function work? We can try a few examples:
Compute (sort [10;9;8;7;6;5;4;3;2;1]).
Example sort_pi: sort [3;1;4;1;5;9;2;6;5;3;5]
= [1;1;2;3;3;4;5;5;5;6;9].
Proof. simpl. reflexivity. Qed.
Compute (insert 7 [1; 3; 4; 8; 12; 14; 18]).
(* = 1; 3; 4; 7; 8; 12; 14; 18 *)
The tail of this list, 12::14::18::nil, is not disturbed or
rebuilt by the insert algorithm. The nodes 1::3::4::7::_ are
new, constructed by insert.
If you're having trouble following exactly how the algorithm
works, try working out how the following evalutes on the board:
sort [3;2;1].
Simply believing that this algorithm works isn't enough.
Let's prove it correct!
sort [3;2;1].
Permutations
Inductive Permutation {A : Type} : list A → list A → Prop :=
| perm_nil: Permutation [] []
| perm_skip (x:A) (l l':list A) (H : Permutation l l') : Permutation (x::l) (x::l')
| perm_swap (x y:A) (l:list A) : Permutation (y::x::l) (x::y::l)
| perm_trans (l1 l2 l3 : list A) (H12 : Permutation l1 l2) (H23 : Permutation l2 l3)
: Permutation l1 l3.
Example permutation_eg :
Permutation [true;true;false] [false;true;true].
Proof.
apply perm_trans with [true;false;true].
{ apply perm_skip.
apply perm_swap. }
{ apply perm_swap. }
Qed.
Theorem Permutation_length : ∀ A (l1 l2 : list A),
Permutation l1 l2 → length l1 = length l2.
Proof.
(* FILL IN HERE *) Admitted.
☐
Permutation l1 l2 → length l1 = length l2.
Proof.
(* FILL IN HERE *) Admitted.
☐
- Theorem: if l1 is a permutation of l2, then l1 and l2 have the same length.
- (perm_nil) We have l1 = [] and l2 = [], both of which have
length 0.
- (perm_skip) We have l1 = x::l1' and l2 = x::l2' and
Permutation l1' l2'; our IH shows that length l1' = length
l2'. We have length (x::l1') = length (x::l2') immediately by
the IH.
- (perm_swap) We have l1 = y::x::l and l2 = x::y::l. We have
length (y::x::l) = length (x::y::l) immediately.
- (perm_trans) We have l1 = l and l2 = l'' and a list l' such that Permutation l l' and Permutation l' l''; our IHs show that length l = length l' and length l' = length l''. We can find length l1 = l2 by transitivity of equality and our IHs. Qed.
(perm_nil) | |
Permutation [] [] |
Permutation l l' | (perm_skip) |
Permutation (x::l) (x::l') |
(perm_swap) | |
Permutation (y::x::l) (x::y::l) |
Permutation l l Permutation l' l'' | (perm_trans) |
Permutation l l'' |
l = [] l' = [] | (perm_nil') |
Permutation l l' |
Exercise: 1 star, standard (Permutation_sym)
Lemma Permutation_sym : ∀ A (l1 l2 : list A),
Permutation l1 l2 → Permutation l2 l1.
Proof.
(* FILL IN HERE *) Admitted.
☐
Permutation l1 l2 → Permutation l2 l1.
Proof.
(* FILL IN HERE *) Admitted.
☐
Exercise: 2 stars, standard (Forall_perm)
To close, a useful utility lemma. Prove this by induction; but is it induction on al, or on bl, or on Permutation al bl, or on Forall f al? Some choices are much easier than others! If you're stuck, try a different one.Inductive Forall {A : Type} (P : A → Prop) : list A → Prop :=
Forall_nil : Forall P []
| Forall_cons (x : A) (l : list A) (Hx : P x) (H : Forall P l) : Forall P (x :: l).
Theorem Forall_perm: ∀ {A} (f: A → Prop) al bl,
Permutation al bl →
Forall f al → Forall f bl.
Proof.
(* FILL IN HERE *) Admitted.
☐
Fixpoint everywhere {A:Type} (a:A) (ls:list A) : list (list A) :=
match ls with
| [] ⇒ [[a]]
| h :: t ⇒ (a :: ls) :: (map (fun t' ⇒ h::t') (everywhere a t))
end.
Example everywhere_1_234 :
everywhere 1 [2;3;4] = [[1;2;3;4];
[2;1;3;4];
[2;3;1;4];
[2;3;4;1]].
Proof. reflexivity. Qed.
Suppose we have a list h::t. Given an element h and a list
permutatons ls of the tail t, how might we collect all
possible insertions of h? The function flat_map from
Poly will serve:
Fixpoint flat_map {X Y: Type} (f: X → list Y) (l: list X)
: (list Y) :=
match l with
| [] ⇒ []
| h :: t ⇒ (f h) ++ (flat_map f t)
end.
Fixpoint permutations {A:Type} (ls:list A) : list (list A) :=
match ls with
| [] ⇒ [[]]
| h :: t ⇒ flat_map (everywhere h) (permutations t)
end.
Compute (permutations [1;2;3;4]).
Example permutations_1234 :
permutations [1;2;3;4] =
[(* insert 1 everywhere into 2;3;4 *)
[1; 2; 3; 4];
[2; 1; 3; 4];
[2; 3; 1; 4];
[2; 3; 4; 1];
(* insert 1 everywhere into 3;2;4 *)
[1; 3; 2; 4];
[3; 1; 2; 4];
[3; 2; 1; 4];
[3; 2; 4; 1];
(* insert 1 everywhere into 3;4;2 *)
[1; 3; 4; 2];
[3; 1; 4; 2];
[3; 4; 1; 2];
[3; 4; 2; 1];
(* insert 1 everywhere into 2;4;3 *)
[1; 2; 4; 3];
[2; 1; 4; 3];
[2; 4; 1; 3];
[2; 4; 3; 1];
(* insert 1 everywhere into 4;2;3 *)
[1; 4; 2; 3];
[4; 1; 2; 3];
[4; 2; 1; 3];
[4; 2; 3; 1];
(* insert 1 everywhere into 4;3;2 *)
[1; 4; 3; 2];
[4; 1; 3; 2];
[4; 3; 1; 2];
[4; 3; 2; 1]].
Proof. reflexivity. Qed.
Compute length (everywhere 5 [1;2;3;4]).
: (list Y) :=
match l with
| [] ⇒ []
| h :: t ⇒ (f h) ++ (flat_map f t)
end.
Fixpoint permutations {A:Type} (ls:list A) : list (list A) :=
match ls with
| [] ⇒ [[]]
| h :: t ⇒ flat_map (everywhere h) (permutations t)
end.
Compute (permutations [1;2;3;4]).
Example permutations_1234 :
permutations [1;2;3;4] =
[(* insert 1 everywhere into 2;3;4 *)
[1; 2; 3; 4];
[2; 1; 3; 4];
[2; 3; 1; 4];
[2; 3; 4; 1];
(* insert 1 everywhere into 3;2;4 *)
[1; 3; 2; 4];
[3; 1; 2; 4];
[3; 2; 1; 4];
[3; 2; 4; 1];
(* insert 1 everywhere into 3;4;2 *)
[1; 3; 4; 2];
[3; 1; 4; 2];
[3; 4; 1; 2];
[3; 4; 2; 1];
(* insert 1 everywhere into 2;4;3 *)
[1; 2; 4; 3];
[2; 1; 4; 3];
[2; 4; 1; 3];
[2; 4; 3; 1];
(* insert 1 everywhere into 4;2;3 *)
[1; 4; 2; 3];
[4; 1; 2; 3];
[4; 2; 1; 3];
[4; 2; 3; 1];
(* insert 1 everywhere into 4;3;2 *)
[1; 4; 3; 2];
[4; 1; 3; 2];
[4; 3; 1; 2];
[4; 3; 2; 1]].
Proof. reflexivity. Qed.
Compute length (everywhere 5 [1;2;3;4]).
What does our function permutations have to do with the
inductive proposition Permutation? We can prove that our
function permutations is in some sense complete: everything it
produces is a permutation of the given list.
Exercise: 2 stars, standard (everywhere_perm)
Lemma everywhere_perm : ∀ A (l l' : list A) (x : A),
In l' (everywhere x l) →
Permutation (x :: l) l'.
Proof.
(* FILL IN HERE *) Admitted.
☐
In l' (everywhere x l) →
Permutation (x :: l) l'.
Proof.
(* FILL IN HERE *) Admitted.
☐
Theorem permutations_complete : ∀ A (l l' : list A),
In l' (permutations l) → Permutation l l'.
Proof.
(* FILL IN HERE *) Admitted.
☐
In l' (permutations l) → Permutation l l'.
Proof.
(* FILL IN HERE *) Admitted.
☐
Exercise: 5 stars, advanced, optional (permutations_correct)
It's possible to prove the converse, that Permutation l l' implies In l' (permutations l). Feel free to give it a go, but it's very challenging! ☐Sortedness
Inductive sorted: list nat → Prop :=
| sorted_nil : sorted []
| sorted_1 (x:nat) : sorted [x]
| sorted_cons (x y:nat) (l:list nat) (Hxy : x ≤ y) (H : sorted (y::l))
: sorted (x::y::l).
Example sorted_one_through_four :
sorted [1;2;3;4].
Proof.
(* WORKED IN CLASS *)
apply sorted_cons. { apply le_S. apply le_n. }
apply sorted_cons. { apply le_S. apply le_n. }
apply sorted_cons. { apply le_S. apply le_n. }
apply sorted_1.
Qed.
Is this really the right definition of what it means for a list to
be sorted? One might have thought that it should refer to list
indices, i.e., for valid indices i,j into the list, the ith item
is less than or equal to the jth item:
Fixpoint nth {X:Type} (n:nat) (l:list X) (default:X) : X :=
match n,l with
| _,[] ⇒ default
| 0,h::_ ⇒ h
| (S n'),_::t ⇒ nth n' t default
end.
Example nth_in_list : nth 3 [1;2;3;4;5] 0 = 4.
Proof. reflexivity. Qed.
Example nth_default : nth 7 [1;2;3;4;5] 0 = 0.
Proof. reflexivity. Qed.
Definition sorted' (al: list nat) :=
∀ i j, i < j < length al → nth i al 0 ≤ nth j al 0.
Note: the notation i < j < length al really means i < j ∧ j <
length al:
This is a reasonable definition too. It should be equivalent.
Later on, we'll prove that the two definitions really are
equivalent. For now, let's use the first one to define what it
means to be a correct sorting algorthm.
Definition is_a_sorting_algorithm (f: list nat → list nat) :=
∀ al, Permutation al (f al) ∧ sorted (f al).
That is: the result (f al) should not only be a sorted
sequence, but it should be some rearrangement (Permutation) of the
input sequence.
Proofs with Sortedness
What operations on lists do we have? Let's start with simple ones,
like mapping (map). If we map the successor function S over a
sorted list, the list should still be sorted.
Lemma add1_preserves_sortedness :
preserves_sortedness (map S).
Proof.
intros al H. induction H.
- simpl. apply sorted_nil.
- simpl. apply sorted_1.
- simpl. simpl in IHsorted.
apply sorted_cons.
apply n_le_m__Sn_le_Sm.
apply Hxy.
apply IHsorted.
Qed.
We can do more than add one... we can add two!
Lemma add2_preserves_sortedness :
preserves_sortedness (map (plus 2)).
Proof.
intros al H. induction H.
- simpl. apply sorted_nil.
- simpl. apply sorted_1.
- simpl. simpl in IHsorted.
apply sorted_cons.
apply n_le_m__Sn_le_Sm.
apply n_le_m__Sn_le_Sm.
apply Hxy.
apply IHsorted.
Qed.
There are lots of functions that preserve sortedness. So many, in
fact, that we can characterize a set of them: the monotonic
functions are those that preserve ordering, i.e., if x ≤ y then
f x ≤ f y. Both S and (plus 2) are monotonic.
Every monotonic function preserves sortedness.
Lemma monotonic_preserves_sortedness : ∀ f,
monotonic f → preserves_sortedness (map f).
Proof.
intros f Hf.
intros al H. induction H.
- simpl. apply sorted_nil.
- simpl. apply sorted_1.
- simpl. simpl in IHsorted.
apply sorted_cons.
apply Hf. apply Hxy.
apply IHsorted.
Qed.
Exercise: 3 stars, standard (addn_preserves_sortedness)
Now prove that adding any number preserves sortedness.Lemma addn_preserves_sortedness : ∀ n,
preserves_sortedness (map (plus n)).
Proof.
(* FILL IN HERE *) Admitted.
☐
Exercise: 4 stars, standard, optional (sorted_filter_cons)
Our proof goes in two stages. First, we show that if something is good at the front of a sorted list, it's also good at the front of a filtered sorted list.Lemma sorted_filter_cons : ∀ l p x,
sorted (x :: l) →
sorted (x :: filter p l).
Proof.
(* FILL IN HERE *) Admitted.
☐
Exercise: 3 stars, standard (filter_preserves_sortedness)
Using the (optional) lemma above, we can prove the more general property: (filter p) preserves sortedness for all predicates p.Lemma filter_preserves_sortedness : ∀ p,
preserves_sortedness (filter p).
Proof.
(* FILL IN HERE *) Admitted.
☐
Exercise: 3 stars, standard (bad_function_breaks_sortedness)
Definition bad_function : nat → nat (* REPLACE THIS LINE WITH ":= _your_definition_ ." *). Admitted.
Now prove that your function doesn't preserve sortedness.
Lemma bad_function_breaks_sortedness :
¬ preserves_sortedness (map bad_function).
Proof.
(* FILL IN HERE *) Admitted.
☐
Proof of Correctness
Exercise: 3 stars, standard (insert_perm)
Prove the following auxiliary lemma, insert_perm, which will be useful for proving sort_perm below. Your proof will be by induction, but you'll need some of the permutation facts from the above.Exercise: 4 stars, standard (insert_sorted)
This one is a bit tricky. However, there is just a single induction right at the beginning, and you do not need to use insert_perm or sort_perm. The leb_spec lemma from IndProp.v may come in handy.Theorem insertion_sort_correct:
is_a_sorting_algorithm sort.
Proof.
split. apply sort_perm. apply sort_sorted.
Qed.
Exercise: 2 stars, standard, optional (sort_stable)
It's a nice exercise to prove the idempotence and stability lemmas from the informal work in Coq.
Lemma sort_stable : ∀ l,
sorted l → sort l = l.
Proof.
(* FILL IN HERE *) Admitted.
Corollary sort_idempotent : ∀ l,
sort (sort l) = sort l.
Proof.
(* FILL IN HERE *) Admitted.
☐
sorted l → sort l = l.
Proof.
(* FILL IN HERE *) Admitted.
Corollary sort_idempotent : ∀ l,
sort (sort l) = sort l.
Proof.
(* FILL IN HERE *) Admitted.
☐
Making Sure the Specification is Right
Exercise: 4 stars, standard, optional (sorted_sorted')
Hint: Instead of doing induction on the list al, do induction
on the sortedness of al. This proof is a bit tricky, so
you may have to think about how to approach it, and try out
one or two different ideas.
(* FILL IN HERE *) Admitted.
☐
Here, you can't do induction on the sorted'-ness of the list,
because sorted' is not an inductive predicate.
Proof.
(* FILL IN HERE *) Admitted.
☐
Proving Correctness from the Alternate Spec
- insert_perm, sort_perm
- Forall_perm, Permutation_length
- Permutation_sym, Permutation_trans
- a new lemma Forall_nth, stated below.
Exercise: 3 stars, standard, optional (Forall_nth)
Lemma Forall_nth:
∀ {A: Type} (P: A → Prop) d (al: list A),
Forall P al ↔ (∀ i, i < length al → P (nth i al d)).
Proof.
(* FILL IN HERE *) Admitted.
☐
∀ {A: Type} (P: A → Prop) d (al: list A),
Forall P al ↔ (∀ i, i < length al → P (nth i al d)).
Proof.
(* FILL IN HERE *) Admitted.
☐
(* Prove that inserting into a sorted list yields a sorted list, for
the index-based notion of sorted.
You'll find leb_spec handy. If you find that your context gets
cluttered, you can run clear H to get rid of the hypothesis H;
you can run clear - H to get rid of everything _but_ H. Be
careful---you can't undo these tactics! *)
Lemma insert_sorted':
∀ a l, sorted' l → sorted' (insert a l).
(* FILL IN HERE *) Admitted.
☐
the index-based notion of sorted.
You'll find leb_spec handy. If you find that your context gets
cluttered, you can run clear H to get rid of the hypothesis H;
you can run clear - H to get rid of everything _but_ H. Be
careful---you can't undo these tactics! *)
Lemma insert_sorted':
∀ a l, sorted' l → sorted' (insert a l).
(* FILL IN HERE *) Admitted.
☐
The Moral of This Story
(* Mon Apr 13 11:08:03 PDT 2020 *)