我开始学习 Clojure,并试图实现一些基本的数值导数函数以供练习。我正在尝试创建一个gradient
接受 n 变量函数和评估它的点的函数。为了以“功能”风格做到这一点,我想将渐变实现为map
1 变量导数。
一元导数函数很简单:
(defn derivative
"Numerical derivative of a univariate function."
[f x]
(let [eps 10e-6] ; Fix epsilon, just for starters.
; Centered derivative is [f(x+e) - f(x-e)] / (2e)
(/ (- (f (+ x eps)) (f (- x eps))) (* 2 eps))))
我想沿着这些线设计渐变:
(defn gradient
"Numerical gradient of a multivariate function."
[f & x]
(let [varity-index (range (count x))
univariate-in-i (fn [i] (_?_))] ; Creates a univariate fn
; of x_i (other x's fixed)
;; For each i = 0, ... n-1:
;; (1) Get univariate function of x_i
;; (2) Take derivative of that function
;; Gradient is sequence of those univariate derivatives.
(map derivative (map univariate-in-i varity-index) x)))
因此,gradient
具有可变的 arity(可以接受任何数量的 x),以及 x 的计数顺序。该函数univariate-in-i
接受一个索引i = 0, 1, ... n-1
并通过部分输出除 之外的所有变量来返回一个 1 变量函数x_i
。例如,你会得到:
#(f x_0 x_1 ... x_i-1 % x_i+1 ... x_n)
^
(x_i still variable)
map
-ping 这个函数varity-index
在 each 中得到一个 1 变量函数序列x_i
,然后对这些函数进行map
-ping得到每个导数序列,这是我们想要的梯度。derivative
x_i
我的问题是:我不确定实施的好方法univariate-in-i
是什么。我基本上需要填写 x 的值,f
除了在某个特定位置(即,放置%
上面),但以编程方式。
我对技术比对解决方案更感兴趣(即,我知道如何计算梯度,我正在尝试学习函数式编程和惯用的 Clojure)。因此,我想坚持将梯度视为偏出函数上的一维导数的映射的策略。但是,如果有更好的“功能”方法,请告诉我。如果可能的话,我宁愿不求助于宏。
提前致谢!
更新:
使用下面 Ankur 的答案,我得到的梯度函数是:
(defn gradient
"Numerical gradient of a multivariate function."
[f & x]
(let [varity-index (range (count x))
x-vec (vec x)
univariate-in-i
(fn [i] #(->> (assoc x-vec i %) (apply f)))]
(map derivative (map univariate-in-i varity-index) x)))
这正是我所希望的,并且看起来非常简洁和实用。