Vector 两个先验排序向量相交的惯用/高效Clojure方法?
我有一对向量Vector 两个先验排序向量相交的惯用/高效Clojure方法?,vector,clojure,Vector,Clojure,我有一对向量x和y,它们是唯一的项,我知道每个项都要排序。我希望两者相交,维持排序顺序。理想的结果是另一个向量,用于快速随机访问 下面的生成只是为了举例,myx和y将进行预排序和预区分(它们实际上是时间样本) 我知道Clojure有Clojure.set/intersection,可以处理排序集。我的x和y具有相同的属性(排序的不同元素),但类型不同 问题1:如果x和y转换为排序集比(应用排序集x)更好/更快,因为它们已经是不同的和排序的吗? user=> (time (def ssx (
x
和y
,它们是唯一的项,我知道每个项都要排序。我希望两者相交,维持排序顺序。理想的结果是另一个向量,用于快速随机访问
下面的生成只是为了举例,myx
和y
将进行预排序和预区分(它们实际上是时间样本)
我知道Clojure有Clojure.set/intersection
,可以处理排序集
。我的x
和y
具有相同的属性(排序的不同元素),但类型不同
问题1:如果x
和y
转换为排序集
比(应用排序集x)
更好/更快,因为它们已经是不同的和排序的吗?
user=> (time (def ssx (apply sorted-set x)))
"Elapsed time: 607.642592 msecs"
user=> (time (def ssy (apply sorted-set y)))
"Elapsed time: 617.046022 msecs"
(defn intersect-sorted-vector [x y]
(loop [x (seq x) y (seq y) acc []]
(if (and x y)
(let [x1 (first x)
y1 (first y)]
(cond
( < x1 y1) (recur (next x) y acc)
( > x1 y1) (recur x (next y) acc)
:else (recur (next x) (next y) (conj acc x1))))
acc)))
现在我已准备好执行交叉
user=> (time (count (clojure.set/intersection ssx ssy)))
"Elapsed time: 355.42534 msecs"
39992
这在性能上有些令人失望,粗略地看一下(source clojure.set/intersection)
似乎没有显示出对这些集合进行排序这一事实的任何特殊处理
问题2:是否有比clojure.set/intersection
更好/更快的方法来执行排序集
s的交集?
user=> (time (def ssx (apply sorted-set x)))
"Elapsed time: 607.642592 msecs"
user=> (time (def ssy (apply sorted-set y)))
"Elapsed time: 617.046022 msecs"
(defn intersect-sorted-vector [x y]
(loop [x (seq x) y (seq y) acc []]
(if (and x y)
(let [x1 (first x)
y1 (first y)]
(cond
( < x1 y1) (recur (next x) y acc)
( > x1 y1) (recur x (next y) acc)
:else (recur (next x) (next y) (conj acc x1))))
acc)))
但是,我忍不住觉得我的代码过于程序化/迭代化
问题3:有谁能建议一种更惯用的方法来处理Clojure中的一对向量吗?通常情况下,快速Clojure代码看起来有点必要。函数式代码通常是优雅的,但会带来一些相关的性能成本,您必须为此付出代价(惰性、来自被丢弃的不可变对象的额外GC压力等)
user=> (time (def ssx (apply sorted-set x)))
"Elapsed time: 607.642592 msecs"
user=> (time (def ssy (apply sorted-set y)))
"Elapsed time: 617.046022 msecs"
(defn intersect-sorted-vector [x y]
(loop [x (seq x) y (seq y) acc []]
(if (and x y)
(let [x1 (first x)
y1 (first y)]
(cond
( < x1 y1) (recur (next x) y acc)
( > x1 y1) (recur x (next y) acc)
:else (recur (next x) (next y) (conj acc x1))))
acc)))
而且,转换成套装的成本总是会更高。构建集合本身就是一个O(n log n)
操作,但是您可以利用向量已经得到支持的事实,在O(n)
时间内实现交集操作
您的代码已经相当不错了,但您还可以进行一些优化:
- 使用向量来收集结果。对于许多连续的conj操作,它们比常规的持久性向量快一点
- 使用原语到向量的索引访问,而不是使用first/next遍历序列。这样可以避免创建临时的seq对象(和相关的GC)
(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
(定义相交排序向量[xy]
(环路[i(长0)、j(长0)、r(瞬态[])]
(设[xi(n次x i零),yj(n次y j零)]
(续)
(不(和席YJ))(坚持!R)
(席YJ)(ReCurr(Inc)J R)
(>席YJ)(ReCuri I(Inc J)R)
:else(recur(inc i)(inc j)(conj!r xi()())))
(时间(计数(相交排序向量x y)))
=>“运行时间:5.143687毫秒”
=> 40258
如您所见,这可能会给您带来6-8倍左右的额外加速。非常感谢您的回复。在我继续学习Clojure的过程中,你给了我很多思考和研究的机会。然而,我认为这里的额外速度很大程度上是由于您优越的运行时环境。在我的系统上,这需要同样多的时间(几乎到毫秒),或者,可能是相同的,只是重复运行我的代码会在你之前触发GC。我得进一步探索。好东西!我怀疑mikera尝试使用primitive
long
s会让他慢下来,因为get
不支持primitive。因此,他不断地对整数进行装箱和拆箱,这与他删除的下一次调用的成本差不多。我预测,用nth
和int
来代替会加快速度。@amalloy-好地方!切换到NTH,它似乎又提供了X2性能提升。我想你想写<代码>(不(和席YJ))< /C>。或
版本在上出现NullPointerException时失败(intersect sorted vector[1 2 3][2 3 4])