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


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


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.



Lemma Permutation_merge : l1 l2, Permutation (merge l1 l2) (l1++l2).

Lemma Permutation_merge_list_to_stack : stack l,
  Permutation (flatten_stack (merge_list_to_stack stack l)) (l ++ flatten_stack stack).

Lemma Permutation_merge_stack : stack,
  Permutation (merge_stack stack) (flatten_stack stack).

Lemma Permutation_iter_merge : l stack,
  Permutation (iter_merge stack l) (flatten_stack stack ++ l).

Theorem sort_perm l : Permutation (sort l) l.

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).

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).

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).

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).

Lemma Sorted_sort : l
  (TOTAL: x y, In x l In y l cmp x y = true cmp y x = true),
   Sorted (sort l).

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).

End MSort.

This page has been generated by coqdoc