Idiomatic/Efficient Clojure way to intersect two a priori sorted vectors?

前端 未结 1 486
滥情空心
滥情空心 2021-02-08 11:26

I have a pair of vectors x and y of unique items, each of which I know to be sorted. I wish to have the intersection of the two, maintaining sort order

相关标签:
1条回答
  • 2021-02-08 12:08

    It is often the case that fast Clojure code looks a bit imperative. Functional code is often elegant, but comes with some associated performance costs that you have to pay for (laziness, extra GC pressure from discarded immutable objects etc.)

    Also, converting into sets is always going to be more expensive. Building a set is an O(n log n) operation in itself, but you can exploit the fact that the vectors are already supported to implement an intersection operation in O(n) time.

    Your code is already pretty good, but there are still a couple more optimisations you can do:

    • Use a transient vector to collect the results. These are a bit faster than regular persistent vectors for lots of sequential conj operations.
    • Used indexed access with primitives into the vectors rather than traversing a sequence with first/next. This avoids creating temporary seq objects (and related GC)

    Resulting code might look something like:

    (defn intersect-sorted-vector [x y]
      (loop [i (long 0), j (long 0), r (transient [])]
        (let [xi (nth x i nil), yj (nth y j nil)]
          (cond 
            (not (and xi yj)) (persistent! r)
            (< xi yj) (recur (inc i) j r)
            (> xi yj) (recur i (inc j) r)
            :else (recur (inc i) (inc j) (conj! r xi))))))
    
    (time (count (intersect-sorted-vector x y)))
    => "Elapsed time: 5.143687 msecs"
    => 40258
    

    So as you can see, this probably gives you an extra 6-8x speedup or thereabouts.

    0 讨论(0)
提交回复
热议问题