在clojure [script]中,如何返回2个排序向量之间的最近元素

时间:2014-03-05 18:14:47

标签: algorithm clojure clojurescript

clojure[script]中,如何编写一个函数nearest,它接收两个已排序的向量 ab并返回{的每个元素{1}} a的最近元素?

举个例子,

b

我希望解决方案兼具惯用性和良好的表现:(nearest [1 2 3 101 102 103] [0 100 1000]); [0 0 0 100 100 100] 是不可接受的!

3 个答案:

答案 0 :(得分:4)

使用二进制搜索或排序集会产生O(n * log m)时间复杂度,其中n为(count a)和m (count b)

然而,利用a和b排序的事实,时间复杂度可以是O(max(n,m))。

(defn nearest [a b]
  (if-let [more-b (next b)]
    (let [[x y] b
          m (/ (+ x y) 2)
          [<=m >m] (split-with #(<= % m) a)]
      (lazy-cat (repeat (count <=m) x)
        (nearest >m more-b)))
    (repeat (count a) (first b))))


=> (nearest [1 2 3 101 102 103 501 601] [0 100 1000])
(0 0 0 100 100 100 100 1000)

答案 1 :(得分:3)

n(count a)m(count b)。然后,如果ab两个,那么这可以在我认为应该O(n log(log m))时间完成,换句话说,非常接近在n中线性化。

首先,让我们重新实现abs和二进制搜索(改进here)独立于主机(利用本机,例如Java,版本应该明显更快)

(defn abs [x] 
  (if (neg? x) (- 0 x) x))

(defn binary-search-nearest-index [v x]
  (if (> (count v) 1)
    (loop [lo 0 hi (dec (count v))]
      (if (== hi (inc lo))
        (min-key #(abs (- x (v %))) lo hi)
        (let [m (quot (+ hi lo) 2)] 
          (case (compare (v m) x) 
             1 (recur lo m) 
            -1 (recur m hi)
             0 m))))
    0))

如果b已排序,则b中的二进制搜索需要log m步。因此,将此映射到a是一个O(n log m)解决方案,对于实用主义者而言可能已经足够了。

(defn nearest* [a b]
  (map #(b (binary-search-nearest-index b %)) a))

但是,我们也可以使用a排序以分割和征服a的事实。

(defn nearest [a b]
  (if (< (count a) 3)
    (nearest* a b)
    (let [m (quot (count a) 2)
          i (binary-search-nearest-index b (a m))] 
      (lazy-cat
        (nearest (subvec a 0 m) (subvec b 0 (inc i))) 
        [(b i)]
        (nearest (subvec a (inc m)) (subvec b i))))))

我认为这应该是O(n log(log m))。我们从a的中位数开始,并在b时间内找到距log m最近的位置。然后我们以a的分割部分递归b的每一半。如果每次分割m - 比例因子b,则有O(n log log m)。如果仅分离出一个常数部分,则在该部分上工作的a的一半是线性时间。如果继续(对b的常量大小部分的工作迭代一半),那么你有O(n)

答案 2 :(得分:1)

受@ amalloy的启发,我找到了这个interesting idea by Chouser并写了这个解决方案:

(defn abs[x]                         
  (max x (- x)))                     

(defn nearest-of-ss [ss x]           
  (let [greater (first (subseq ss >= x))
        smaller (first (rsubseq ss <= x))]
    (apply min-key #(abs (- % x)) (remove nil? [greater smaller]))))

(defn nearest[a b]
  (map (partial nearest-of-ss (apply sorted-set a)) b))

备注:创建sorted-set一次非常重要,以避免性能下降!