-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmerge-sort-compare.ml
158 lines (119 loc) · 4.26 KB
/
merge-sort-compare.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
(* OCaml program to compare, for a given list length, len:
- the worst case number of comparisons used by merge sort;
- wc len;
- len * int_log 2 len *)
open Batteries
open List
(* return the list consisting of 0 .. n - 1 *)
let rec upto (n : int) : int list =
match n with
| 0 -> []
| n -> upto (n - 1) @ [n - 1]
(* return all the lists formed by inserting x into ys in some position
*)
let rec all_insert (x : 'a) (ys : 'a list) : 'a list list =
match ys with
| [] -> [[x]]
| y :: ys ->
[x :: y :: ys] @
map (fun zs -> y :: zs) (all_insert x ys)
(* return all the permutations of xs *)
let rec all_perms (xs : 'a list) : 'a list list =
match xs with
| [] -> [[]]
| x :: xs ->
concat (map (all_insert x) (all_perms xs))
(* turn a permutation on 0 .. length perm - 1 into a comparison
function on elements of this range *)
let cmp_of_perm (perm : 'a list) (x : int) (y : int) : bool =
match index_of x perm with
| None -> raise (Failure "should not happen")
| Some i ->
match index_of y perm with
| None -> raise (Failure "should not happen")
| Some j -> i <= j
(* assuming perm is a permutation on 0 .. length perm - 1, the
elements of xs and ys are in the range 0 .. length perm - 1, and xs
and ys are sorted according to cmp_of_perm perm, merge xs and ys so
the result is sorted according to cmp_of_perm perm; for each
comparison carried out, increment the contents of cr *)
let rec merge
(perm : int list) (cr : int ref) (xs : int list) (ys : int list)
: int list =
match xs with
| [] -> ys
| x :: xs ->
match ys with
| [] -> x :: xs
| y :: ys ->
(cr := !cr + 1;
if cmp_of_perm perm x y
then x :: merge perm cr xs (y :: ys)
else y :: merge perm cr (x :: xs) ys)
(* assuming perm is a permutation on 0 .. length perm - 1, sort xs
according to cmp_of_perm perm; for each comparison carried out,
increment the contents of cr *)
let rec merge_sort (perm : int list) (cr : int ref) (xs : int list) =
match xs with
| [x] -> [x]
| xs ->
let mid = List.length xs / 2 in
let ys = List.take mid xs in
let zs = List.drop mid xs in
merge perm cr (merge_sort perm cr ys) (merge_sort perm cr zs)
(* given a permutation on 0 .. length perm - 1, use merge_sort
to sort (upto (length perm)) according to cmp_of_perm perm,
returning only the number of comparisons carried out *)
let run_merge_sort_on_upto (perm : 'a list) : int =
let cr = ref 0 in
let _ = merge_sort perm cr (upto (length perm)) in
!cr
(* exponentiation *)
let rec pow (n : int) (m : int) : int =
if m = 0
then 1
else n * pow n (m - 1)
(* auxiliary function for int_log 2 *)
let rec il_find (n : int) (i : int) : int =
if pow 2 i <= n && n < pow 2 (i + 1)
then i
else il_find n (i + 1)
(* int_log 2 *)
let il (n : int) : int =
if n <= 0
then raise (Failure "arg must be at least 1")
else il_find n 0
(* integer division, rounding up *)
let divup (n : int) (m : int) : int =
n / m + (if n mod m = 0 then 0 else 1)
(* upper bound on worst case number of comparisons carried out by
merge sort when sorting a list of length n whose elements are in
the range 0 .. n - 1 according to the comparison function
corresponding to a permutation of upto n *)
let rec wc (n : int) : int =
if n <= 1
then 0
else wc (n / 2) + wc (divup n 2) + n - 1
(* collect
- maximum over all permutations, perm, on 0 .. len - 1 of the
number of comparisons carried out when sorting upto n according to
the comparison function of perm;
- wc len;
- len * int_log 2 len *)
let compare (len : int) : int * (int * int * int) =
(len,
(max (map run_merge_sort_on_upto (all_perms (upto len))),
wc len,
len * il len))
(* print a result of compare *)
let pr_compare ((len, (i, j, k)) : int * (int * int * int)) : unit =
Printf.printf "%2d: %2d %2d %2d\n" len i j k
(* run compare on all the numbers between 1 and n *)
let rec compare_range (n : int) : (int * (int * int * int)) list =
if n = 0
then []
else compare_range (n - 1) @ [compare n]
(* run compare on all the numbers between 1 and n, printing
the results *)
let run_compare_range (n : int) : unit =
List.iter pr_compare (compare_range n)