我想出了这个:
(def kernel [0 1 1 2 3 3 0 0 0 0 0 0])
(def data [1 5 7 4 8 3 9 5 6 3 2 1 1 7 4 9 3 2 1 8 6 4])
(defn capped+ [a b c] (let [s (+ a b)] (if (> s c) c s)))
(defn *+ [a b]
(if (> (count a) (count b))
(reduce + (map-indexed (fn _ [i x] (* (a i) (b i))) b))
(reduce + (map-indexed (fn _ [i x] (* (a i) (b i))) a))))
(defn slide*i [d k]
(let [ki (into [] (reverse k)) kl (count k) dl (count d)]
(map-indexed
(fn [idx item]
(/ (*+ ki (subvec d idx (capped+ idx kl dl)))
(reduce + ki)))
d)))
(def s (slide*i data kernel))
这不是最优雅的代码,但它工作正常。 我其实想用它来抚平一些非常尖刻!数据
任何建议,以使这更美丽或更有效或更准确(个人我不关心尾巴是不准确的,因为在我的情况下我从来没有使用它)受到欢迎。
答案 0 :(得分:7)
您当然可以显着提高此操作的性能。好消息是你不需要为此而放弃Java:如果你正确地优化它,Clojure非常快,并且在大多数情况下可以产生与纯Java相同的速度。
为了在Clojure中获得最佳的数字代码性能,您需要使用:
以下内容应该是正确的,并且可能会使您获得与Java大致相同的性能:
(def kernel (double-array [0 1 1 2 3 3 0 0 0 0 0 0]))
(def data (double-array [1 5 7 4 8 3 9 5 6 3 2 1 1 7 4 9 3 2 1 8 6 4]))
(defn convolve [^doubles kernel-array ^doubles data-array]
(let [ks (count kernel-array)
ds (count data-array)
output (double-array (+ ks ds))
factor (/ 1.0 (areduce kernel-array i ret 0.0 (+ ret (aget kernel-array i))))]
(dotimes [i (int ds)]
(dotimes [j (int ks)]
(let [offset (int (+ i j))]
(aset output offset (+ (aget output offset) (* factor (* (aget data-array i) (aget kernel-array j))))))))
output))
(seq (convolve kernel data))
=> (0.0 0.1 0.6 1.4 2.4 4.4 5.5 6.1000000000000005 5.600000000000001 6.200000000000001 5.499999999999999 5.9 4.199999999999999 3.3000000000000003 2.5 2.2 3.3 4.4 5.6000000000000005 4.8 4.8999999999999995 3.1 3.5 4.300000000000001 5.0 3.0 1.2000000000000002 0.0 0.0 0.0 0.0 0.0 0.0 0.0)
我没有修剪输出数组或完成任何边界,所以你可能需要稍微破解这个解决方案以获得你想要的输出,但希望你能得到这个想法......
一些非常粗略的基准测试:
(time (dotimes [i 1000] (seq (convolve kernel data))))
=> "Elapsed time: 8.174109 msecs"
即。每个内核/数据对组合大约30ns - 我希望这几乎可以达到缓存内存访问的范围。
答案 1 :(得分:4)
; unused, but left for demonstration (defn capped+ [a b c] (min (+ a b) c)) (defn *+ [a b] (reduce + (map * a b))) (defn slide*i [d k] (let [ki (reverse k) kl (count k) ks (reduce + k)] (map #(/ (*+ %1 ki) ks) (partition kl 1 d))))
使用partition
,结果为:
(59/10 21/5 33/10 5/2 11/5 33/10 22/5 28/5 24/5 49/10 31/10)
但是使用partition-all
,您将获得您的解决方案所带来的确切结果:
(59/10 21/5 33/10 5/2 11/5 33/10 22/5 28/5 24/5 49/10 31/10 7/2 43/10 5 3 6/5 0 0 0 0 0 0)
答案 2 :(得分:1)
这样做的有效方法是创建进行卷积并从clojure调用它的java类,如果可能的话将它传递给java数组。如果效率是一个问题,Clojure实现也应该在java数组上运行。