gpt4 book ai didi

vector - 在 clojure 中,通过内核对向量进行卷积的有效方法是什么?

转载 作者:行者123 更新时间:2023-12-04 22:15:28 24 4
gpt4 key购买 nike

我想出了这个:

(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))

这不是最优雅的代码,但它运行良好。
我其实想用它来平滑一些非常尖的!数据。

欢迎任何使这个更漂亮或更高效或更准确的建议(我个人不关心尾部不准确,因为在我的情况下我从不使用它)。

最佳答案

您当然可以显着提高此操作的性能。好消息是,您不需要为此投入 Java:如果您正确优化 Clojure,它的速度非常快,并且在大多数情况下可以产生与纯 Java 相同的速度。

为了在 Clojure 中实现数字代码的最大性能,您需要使用:

  • 数组 ,因为您需要具有非常快的写入和查找速度的可变存储。 Clojure 序列和向量很漂亮,但它们带来了你可能想要避免的开销,这些开销对于真正对性能至关重要的代码
  • 原语,因为它们提供更快的数学。
  • aset/aget/areduce - 这些是为数组设计的极快的操作,基本上为您提供与纯 Java 等效项相同的字节码。
  • 命令式风格 - 虽然它在 Clojure 中是单语的,但它获得最快的结果(主要是因为您可以避免内存分配、装箱和函数调用的开销)。一个例子是使用 dotimes 进行快速命令式循环。
  • (set! *warn-on-reflection* true) - 消除你的代码产生的任何警告,因为反射是一个很大的性能杀手。

  • 以下应该是正确的,并且可能会让您获得与 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 - 我预计这几乎达到了缓存内存访问的界限。

    关于vector - 在 clojure 中,通过内核对向量进行卷积的有效方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8251030/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com