1

I implemented quicksort in OCaml. Here is the code:


let shuffle d =
    let nd = List.map (fun c -> (Random.bits (), c)) d in
    let sond = List.sort compare nd in
    List.map snd sond;;

let partition = function
  | [] -> ([], [], [])
  | pivot::tl ->
    let rec p (left, right) = function
      | [] -> (left, right, [pivot])
      | first::rest ->
    let c = compare pivot first
    in 
    if c > 0 then
      p (first::left, right) rest
    else 
      p (left, first::right) rest
    in
    p ([], []) tl;;

let quicksort l =
  let sl = shuffle l 
  in
  let rec qs = function
    | [] -> []
    | l ->
      let (left, right, pivot) = partition l
      in 
      (qs left) @ pivot @ (qs right)
  in 
  qs sl;;

First, I think maybe there is a better way to implement partition. List.partition came to my mind, but I just wanted to implement the key part by myself

Second, I use @ a lot in the sorting which is inefficient, right?

any suggestions?


Edit

One more question to think about is whether 3-way quicksort affects the implementation in OCaml?

Jackson Tale
  • 25,428
  • 34
  • 149
  • 271
  • 2
    (For what it's worth, the brilliance of quicksort is how well it exploits an underlying mutable array representation. It's not clear there's a reason to use it with lists.) – Jeffrey Scofield Feb 26 '13 at 20:20
  • @JeffreyScofield oh, ok. I am just trying to practising my OCaml skills. I thought we should avoid using Array, right? – Jackson Tale Feb 26 '13 at 22:27
  • @JeffreyScofield also, Array.length is O(1) or O(n)? – Jackson Tale Feb 26 '13 at 22:32
  • Array.length is O(1). – Jeffrey Scofield Feb 26 '13 at 22:36
  • could you please tell me why OCaml's list uses MergeSort not quicksort? – Jackson Tale Feb 26 '13 at 22:50
  • IMHO, the brilliance of quicksort is the method of segregating by moving two indices from the ends toward the middle of an array. It's just amazingly simple and effective (once you've chosen your pivot value). There's no way to get this effect with lists. For lists, mergesort is the brilliantly simple solution (again, IMHO). At any rate, the easy answer is that mergesort is probably faster. But I don't have any inside knowledge about the choice. – Jeffrey Scofield Feb 26 '13 at 22:57
  • I think that another point is that if you're unlucky in your choice for a pivot value, quicksort runs in O(n²). On the other hand, mergesort will always be in O(n.log(n)). For a standard library function, having a better worst-case complexity is IMHO an interesting feature. – Virgile Feb 27 '13 at 07:48
  • @Virgile normally the list will be shuffled before hand, right? – Jackson Tale Feb 27 '13 at 09:12
  • Yes, but since the shuffle will be in O(n.log(n)) as well (see your previous question on the subject http://stackoverflow.com/questions/15095541/how-to-shuffle-list-in-on-in-ocaml), it's as easy to make the shuffling order the list rather than having a random shuffle and reorder everything afterwards. Besides, if you want to implement `List.sort` that way, you'd have to implement the `shuffle` function without using `List.sort` in the first place ;-) – Virgile Feb 27 '13 at 17:34
  • Having tried it, a 3 way quicksort doesn't seem to bring any significant improvement on a single CPU core, but it's interesting to implement. – didierc Mar 01 '13 at 23:50

2 Answers2

2

The p function is badly indented; speaking of indentation, I tend to think the style of having a in on the next line is overkill for single-line declarations, so I'd rather put them at the end of the one-liner declaration.

More importantly, there is no need for it to take a tuple of lists as arguments, you'll have something syntactically lighter using two separate (curried) arguments. You could also use the List.partition function of the standard library.

gasche
  • 31,259
  • 3
  • 78
  • 100
2

A micro optimization that you can try is to do List.concat[[qs left]; [pivot]; [qs right]] to append the lists at once buth you will need to run some benchmarks to verify this even helps.

rgrinberg
  • 9,638
  • 7
  • 27
  • 44
  • 2
    If you want to play this game, you can have the left recursive call use the *reversed* comparison function, so that you can use `List.rev_append (qs left) (pivot :: qs right)`. – gasche Feb 26 '13 at 22:13