我想知道是否有人可以帮助我在Clojure 1.3中使用此代码段的性能。我正在尝试实现一个简单的函数,它需要两个向量并完成一系列产品。
因此,假设向量是X(大小为10,000个元素)和B(大小为3个元素),并且乘积之和存储在向量Y中,数学上看起来像这样:
Y0 = B0 * X2 + B1 * X1 + B2 * X0
Y1 = B0 * X3 + B1 * X2 + B2 * X1
Y2 = B0 * X4 + B1 * X3 + B2 * X2
依旧......
对于此示例,Y的大小最终为9997,对应于(10,000 - 3)。我已经设置了接受任何大小的X和B的函数。
以下是代码:它基本上从X一次获取(count b)
个元素,将其反转,将*
映射到B并对结果序列的内容求和,以生成Y元素。
(defn filt [b-vec x-vec]
(loop [n 0 sig x-vec result []]
(if (= n (- (count x-vec) (count b-vec)))
result
(recur (inc n) (rest sig) (conj result (->> sig
(take (count b-vec))
(reverse)
(map * b-vec)
(apply +)))))))
让X为(vec (range 1 10001))
且B为[1 2 3]
时,此功能大约需要6秒才能运行。我希望有人可以建议改进运行时间,无论是算法,还是我可能滥用的语言细节。
谢谢!
P.S。我已完成(set! *warn-on-reflection* true)
但未收到任何反映警告信息。
答案 0 :(得分:7)
你不必多次使用计数。下面的代码计算只计算一次
(defn filt [b-vec x-vec]
(let [bc (count b-vec) xc (count x-vec)]
(loop [n 0 sig x-vec result []]
(if (= n (- xc bc))
result
(recur (inc n) (rest sig) (conj result (->> sig
(take bc)
(reverse)
(map * b-vec)
(apply +))))))))
(time (def b (filt [1 2 3] (range 10000))))
=> "Elapsed time: 50.892536 msecs"
答案 1 :(得分:6)
如果你真的想要这种计算的最佳性能,你应该使用数组而不是矢量。阵列具有许多性能优势:
代码如下:
(defn filt [^doubles b-arr
^doubles x-arr]
(let [bc (count b-arr)
xc (count x-arr)
rc (inc (- xc bc))
result ^doubles (double-array rc)]
(dotimes [i rc]
(dotimes [j bc]
(aset result i (+ (aget result i) (* (aget x-arr (+ i j)) (aget b-arr j))))))
result))
答案 2 :(得分:3)
要继续使用Ankur的优秀答案,您还可以避免重复调用反向功能,这样可以让我们获得更高的性能。
(defn filt [b-vec x-vec]
(let [bc (count b-vec) xc (count x-vec) bb-vec (reverse b-vec)]
(loop [n 0 sig x-vec result []]
(if (= n (- xc bc))
result
(recur (inc n) (rest sig) (conj result (->> sig
(take bc)
(map * bb-vec)
(apply +))))))))