A modular implementation of mergesort (the complexity is O(n.log n) in the length of the list)


Require Import Vbase List Setoid Permutation Sorted Orders.

Set Implicit Arguments.

Notations and conventions

Local Notation "[ ]" := nil.
Local Notation "[ a ; .. ; b ]" := (a :: .. (b :: []) ..).

Open Scope bool_scope.

Section MSort.

Variable A : Type.
Variable cmp : A A bool.

Fixpoint merge l1 l2 :=
  let fix merge_aux l2 :=
  match l1, l2 with
  | [], _l2
  | _, []l1
  | a1::l1', a2::l2'
      if cmp a1 a2 then a1 :: merge l1' l2 else a2 :: merge_aux l2'
  end
  in merge_aux l2.

We implement mergesort using an explicit stack of pending mergings. Pending merging are represented like a binary number where digits are either None (denoting 0) or Some list to merge (denoting 1). The n-th digit represents the pending list to be merged at level n, if any. Merging a list to a stack is like adding 1 to the binary number represented by the stack but the carry is propagated by merging the lists. In practice, when used in mergesort, the n-th digit, if non 0, carries a list of length 2^n. For instance, adding singleton list 3 to the stack Some 4::Some 2;6::None::Some 1;3;5;5 reduces to propagate the carry 3;4 (resulting of the merge of 3 and 4) to the list Some 2;6::None::Some 1;3;5;5, which reduces to propagating the carry 2;3;4;6 (resulting of the merge of 3;4 and 2;6) to the list None::Some 1;3;5;5, which locally produces Some 2;3;4;6::Some 1;3;5;5, i.e. which produces the final result None::None::Some 2;3;4;6::Some 1;3;5;5.
For instance, here is how 6;2;3;1;5 is sorted:
       operation             stack                list
       iter_merge            []                   [6;2;3;1;5]
    =  append_list_to_stack  [ + [6]]             [2;3;1;5]
    -> iter_merge            [[6]]                [2;3;1;5]
    =  append_list_to_stack  [[6] + [2]]          [3;1;5]
    =  append_list_to_stack  [ + [2;6];]          [3;1;5]
    -> iter_merge            [[2;6];]             [3;1;5]
    =  append_list_to_stack  [[2;6]; + [3]]       [1;5]
    -> merge_list            [[2;6];[3]]          [1;5]
    =  append_list_to_stack  [[2;6];[3] + [1]     [5]
    =  append_list_to_stack  [[2;6] + [1;3];]     [5]
    =  append_list_to_stack  [ + [1;2;3;6];;]     [5]
    -> merge_list            [[1;2;3;6];;]        [5]
    =  append_list_to_stack  [[1;2;3;6];; + [5]]  []
    -> merge_stack           [[1;2;3;6];;[5]]
    =                                             [1;2;3;5;6]
The complexity of the algorithm is n*log n, since there are 2^(p-1) mergings to do of length 2, 2^(p-2) of length 4, ..., 2^0 of length 2^p for a list of length 2^p. The algorithm does not need explicitly cutting the list in 2 parts at each step since it the successive accumulation of fragments on the stack which ensures that lists are merged on a dichotomic basis.

Fixpoint merge_list_to_stack stack l :=
  match stack with
  | [][Some l]
  | None :: stack'Some l :: stack'
  | Some l' :: stack'None :: merge_list_to_stack stack' (merge l' l)
  end.

Fixpoint merge_stack stack :=
  match stack with
  | [][]
  | None :: stack'merge_stack stack'
  | Some l :: stack'merge l (merge_stack stack')
  end.

Fixpoint iter_merge stack l :=
  match l with
  | []merge_stack stack
  | a::l'iter_merge (merge_list_to_stack stack [a]) l'
  end.

Definition sort := iter_merge [].

The proof of correctness

Local Notation Sorted := (LocallySorted cmp) (only parsing).

Fixpoint SortedStack stack :=
  match stack with
  | []True
  | None :: stack'SortedStack stack'
  | Some l :: stack'Sorted l SortedStack stack'
  end.

Local Ltac invert H := inversion H; subst; clear H.

Fixpoint flatten_stack (stack : list (option (list A))) :=
  match stack with
  | [][]
  | None :: stack'flatten_stack stack'
  | Some l :: stack'l ++ flatten_stack stack'
  end.

Existing Instance Permutation_app'.

Local Add Parametric Morphism (a: A) : (In a)
  with signature @Permutation A ==> iff
  as in_mor.
Proof. split; eapply Permutation_in; eauto using Permutation_sym. Qed.

Lemma Permutation_merge : l1 l2, Permutation (merge l1 l2) (l1++l2).
Proof.
  induction l1; [by destruct l2; ins|].
  induction l2; rewrite ?app_nil_r; simpl merge; try done; desf; vauto.
  rewrite <- Permutation_middle; eauto.
Qed.

Lemma Permutation_merge_list_to_stack : stack l,
  Permutation (flatten_stack (merge_list_to_stack stack l)) (l ++ flatten_stack stack).
Proof.
  induction stack; ins; desf; ins; rewrite app_assoc.
  by rewrite IHstack, Permutation_merge, (Permutation_app_comm l).
Qed.

Lemma Permutation_merge_stack : stack,
  Permutation (merge_stack stack) (flatten_stack stack).
Proof.
  by induction stack; ins; desf; rewrite Permutation_merge, IHstack.
Qed.

Lemma Permutation_iter_merge : l stack,
  Permutation (iter_merge stack l) (flatten_stack stack ++ l).
Proof.
  induction l; ins; rewrite ?app_nil_r, ?Permutation_merge_stack; ins.
  by rewrite IHl, Permutation_merge_list_to_stack, <- Permutation_middle.
Qed.

Theorem sort_perm l : Permutation (sort l) l.
Proof. exact (Permutation_iter_merge l []). Qed.

Lemma Sorted_merge :
   l1 l2
    (TOTAL: x y, In x l1 In x l2 In y l1 In y l2
                        cmp x y = true cmp y x = true)
    (S1 : Sorted l1)
    (S2 : Sorted l2),
  Sorted (merge l1 l2).
Proof.
  induction l1; induction l2; ins; desf.
    inv S1; vauto.
    assert (Sorted (merge (b::l) (a0::l2))).
      by apply IHl1; ins; eapply TOTAL; tauto.
    by clear S1 S2 IHl1; simpls; desf; vauto.
  assert (cmp a0 a).
    by destruct (TOTAL a0 a); eauto; congruence.
  inv S2; vauto.
  assert (Sorted (merge (a::l1) (b::l))).
    by apply IHl2; ins; eapply TOTAL; tauto.
  clear IHl2; ins; desf; vauto.
Qed.

Lemma Sorted_merge_list_to_stack : stack l
    (TOTAL: x y, In x (flatten_stack stack) In x l
                        In y (flatten_stack stack) In y l
                        cmp x y = true cmp y x = true),
  SortedStack stack Sorted l SortedStack (merge_list_to_stack stack l).
Proof.
  induction stack; ins; desf; ins; desf.
  apply IHstack, Sorted_merge; ins; try rewrite Permutation_merge, in_app_iff in *;
  eapply TOTAL; desf; eauto using in_or_app.
Qed.

Lemma Sorted_merge_stack : stack
    (TOTAL: x y, In x (flatten_stack stack)
                        In y (flatten_stack stack)
                        cmp x y = true cmp y x = true),
  SortedStack stack Sorted (merge_stack stack).
Proof.
  induction stack; ins; desf; desf; vauto.
    apply Sorted_merge, IHstack; ins; eauto using in_or_app.
      by rewrite Permutation_merge_stack in *; eauto using in_or_app.
    by eapply TOTAL; auto using in_or_app.
  by eapply IHstack; eauto; eapply TOTAL; auto using in_or_app.
Qed.

Lemma Sorted_iter_merge : stack l
    (TOTAL: x y, In x (flatten_stack stack) In x l
                        In y (flatten_stack stack) In y l
                        cmp x y = true cmp y x = true),
  SortedStack stack Sorted (iter_merge stack l).
Proof.
  intros until l; revert stack; induction l; ins.
    by eapply Sorted_merge_stack; eauto.
  eapply IHl, Sorted_merge_list_to_stack; ins; vauto.
    rewrite Permutation_merge_list_to_stack in *; ins.
    by eapply TOTAL; desf; eauto.
  by eapply TOTAL; desf; eauto.
Qed.

Lemma Sorted_sort : l
  (TOTAL: x y, In x l In y l cmp x y = true cmp y x = true),
   Sorted (sort l).
Proof.
  ins; apply Sorted_iter_merge; ins; desf; eauto.
Qed.

Theorem sort_sorted : (l: list A)
    (TOTAL: x y, In x l In y l cmp x y = true cmp y x = true)
    (TRANS: transitive _ cmp),
  StronglySorted cmp (sort l).
Proof.
  ins; eapply Sorted_StronglySorted, Sorted_LocallySorted_iff, Sorted_sort; ins.
Qed.

End MSort.

This page has been generated by coqdoc