将带有嵌套 for 循环的伪代码转换为 Clojure

Convert pseudo-code with nested for-loops to Clojure

我想在 Clojure 中实现这个伪代码:

   function(n)                                       
       B[0] <-- 1                                      
       for m <-- 1 to n do                           
           B[m] <-- 0                                  
           for k <-- 0 to m - 1 do                     
               B[m] <-- B[m] − binom(m+1, k) * B[k]   
           B[m] <-- B[m]/(m+1)                               
       return B[n]    

我的第一个想法是做这样的事情:

(defn foo [n]
  (if (= n 0)
    (int 1)
    (for [k (range 0 (- n 1))]
      (* (binom (+ n 1) k)
         (foo k))))) 

但现在我卡住了,我不知道如何继续。当我尝试将它们翻译成 Clojure 时,嵌套循环让我很困惑。

我非常感谢有关如何在 Clojure 中编写这段代码的帮助,我感到有点迷茫。

提前致谢!

伪代码使用了大量 in-place 更新,这使得它有点难以阅读。但本质上,该代码计算一个数字列表,其中每个数字都是根据列表中的先前数字计算得出的。然后我们从列表中选择一个号码。

我会像这样实现这个算法

(defn compute-next-B [B]
  (let [m (count B)
        m+1 (inc m)
        terms (map-indexed (fn [k Bk] (- (* Bk (binom m+1 k)))) B)]
    (conj B (/ (apply + terms) m+1))))

(defn foo [n]
  (->> [1]
       (iterate compute-next-B)
       (drop n)
       first
       last))

伪代码的外循环是由(iterate compute-next-B ...)生成的惰性序列。伪代码的内部循环是惰性序列 terms.

(apply + terms) 内的迭代

给定的伪代码计算第 n 个 Bernoulli number。它使用所有先前的伯努利数来计算结果。与阶乘函数非常相似,这适用于递归算法,可以使用 memoize 来实现,以避免 re-computation 更早的数字:

(def factorial
  "Returns n!."
  (memoize (fn [n]
             (if (< 1 n)
               (* n (factorial (dec n)))
               1N))))

(def bernoulli
  "Returns the nth Bernoulli number."
  (memoize (fn [n]
             (if (zero? n)
               1
               (let [n!    (factorial n)
                     term  #(/ (* n! (bernoulli %))
                               (factorial %)
                               (factorial (- n % -1)))
                     terms (map term (range n))]
                 (reduce - 0 terms))))))
(map bernoulli (range 9)) ; => (1 -1/2 1/6 0N -1/30 0N 1/42 0N -1/30)

有些算法本质上是命令式的。如果那是最简单的解决方案,不要害怕编写命令式代码,而不是试图将算法“强制适应”到函数式风格。

该算法可以轻松地使用可变原子来存储 B 数组:

(defn factorial [x]
  (reduce * (range 2 (inc x))))

(defn binom [n k]
  (/ (factorial n)
    (factorial k) (factorial (- n k))))

(defn bernoulli [n]
  (let [B (atom (vec (repeat n 0)))] ; allocate B[0]..B[n-1] = zeros
    (swap! B assoc 0 1) ; B[0] = 1
    (doseq [m (range 1 (inc n))] ; 1..n
      (swap! B assoc m 0) ; B[m] = 0
      (doseq [k (range m)] ; 0..(m-1)
        (swap! B #(assoc % m  ; B[m] = ...
                    (-
                      (get % m) ; B[m]
                      (*
                        (binom (inc m) k)
                        (get % k)))))) ; B[k]
      (swap! B update m ; B[m] = B[m] ...
        #(/ % (inc m))))
    (get @B n)))

(dotest
  (dotimes [i 10]
    (spyx [i (bernoulli i)])))

结果


[i (bernoulli i)] => [0 1]
[i (bernoulli i)] => [1 -1/2]
[i (bernoulli i)] => [2 1/6]
[i (bernoulli i)] => [3 0N]
[i (bernoulli i)] => [4 -1/30]
[i (bernoulli i)] => [5 0N]
[i (bernoulli i)] => [6 1/42]
[i (bernoulli i)] => [7 0N]
[i (bernoulli i)] => [8 -1/30]
[i (bernoulli i)] => [9 0N]

您还可以将 with-local-vars 用于某些算法,甚至可以放入(可变的)Java 数组。您可以在 this mutable Java matrix example

中查看示例