在clojure中缓慢'快速排序'

时间:2012-06-08 07:16:38

标签: clojure quicksort

我在clojure中编写了一个快速排序函数,但运行速度非常慢。有时,如果输入集合变得更大,它甚至可能溢出堆栈?

我的代码有什么问题吗?

(defn qsort [coll]
  (if (<= (count coll) 1)
    coll
    (let [pivot (last coll)
          head-half (filter #(< % pivot) (drop-last coll))
          tail-half (filter #(>= % pivot) (drop-last coll))]
      (concat (qsort head-half) (vector pivot) (qsort tail-half)))))

我也读过clojure.core中的sort函数,但它让人感到困惑。

01 (defn sort
02   "Returns a sorted sequence of the items in coll. If no comparator is
03   supplied, uses compare. comparator must
04   implement java.util.Comparator."
05   {:added "1.0"
06    :static true}
07   ([coll]
08    (sort compare coll))
09   ([^java.util.Comparator comp coll]
10    (if (seq coll)
11      (let [a (to-array coll)]
12        (. java.util.Arrays (sort a comp))
13        (seq a))
14      ())))

递归发生的唯一地方是第12行。它只是交换两个输入参数! 你可以向我解释为什么这段代码会运行吗?

4 个答案:

答案 0 :(得分:2)

(. java.util.Arrays (sort a comp))这个调用是Arrays排序函数,而不是对clojure.core排序函数的递归调用。

你的代码很慢,因为快速排序是imperative algorithm,即它基于命令式编程的概念,如就地数组变异。您的代码很好,但问题是它遍历集合多次,并且还创建了许多中间集合。

您可以使用数组和就地数组变异来快速排序。

答案 1 :(得分:2)

Clojure的sort函数在内部使用Java的标准java.util.Arrays / sort方法;它不是100%的clojure实现。

Quicksort并不是真正的惯用法,因为它取决于具有快速O(1)交换元素的集合类型。还要注意,在你的实现中,你正在做(last coll)和(count coll)每次调用,其中coll是一个懒惰的seq,所以两者都是O(n) - 你应该能够通过这个来改进性能考虑〜可能是通过使用java数组而不是不可变的seq作为中间集合类型。

答案 2 :(得分:1)

堆栈溢出的问题是你是递归地在过滤器上放置延迟过滤器。这在那里解释得很好:

Recursive function causing a stack overflow

关于你的另一个问题:在第12行中,没有调用clojure sort函数,而是调用Arrays-sort函数。在用户代码中,您通常会编写(java.util.Arrays/sort a comp)而不是点语法。

答案 3 :(得分:1)

您的qsort可能会花费大部分时间来分配对象。

  • 对过滤器的调用为每次传递的每个数字分配一个lazy-cons实例
  • 对连接的调用为每个数字分配另一个对象

虽然你的版本在我看来读得更好