2

我开始学习 Clojure,并试图实现一些基本的数值导数函数以供练习。我正在尝试创建一个gradient接受 n 变量函数和评估它的点的函数。为了以“功能”风格做到这一点,我想将渐变实现为map1 变量导数。

一元导数函数很简单:

(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得到每个导数序列,这是我们想要的梯度。derivativex_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)))

这正是我所希望的,并且看起来非常简洁和实用。

4

2 回答 2

2

你可以定义univariate-in-i如下。(假设所有其他位置值都在某个 var default 中定义,它是一个向量)

(fn [i] #(->>
           (assoc default i %)
           (apply f)))
于 2013-03-18T17:06:20.987 回答
0

如果您发现这有点难以理解(在如何实现渐变的上下文中),使用 clojure 的多变量渐变实现的另一个变体: 在此处输入图像描述 那么,给定f and vector v of a1,....,aN,将在除 xi 之外的所有变量都固定的情况下进行区分:

(defn partial-diff [f v i]
  (let [h 10e-6
        w (update v i + h)]
    (/ (- (apply f w) (apply f v))
       h)))

(defn gradient [f v]
  (map #(partial-diff f v %) (range (count v))))

=>

(gradient (fn [x y] 
              (+ (* x x) (* x y y))) [3 3])
=> (15.000009999965867 18.000030000564493)
于 2020-11-30T08:56:04.700 回答