Clojure: превышен лимит накладных расходов gc, ленивая оценка, последовательность pi

Для следующего кода:

(ns clojure101.series)

(defn avg [[x y]] (/ (+ x y) 2))

(defn avg-damp
  [seq]
  (map avg (partition 2 seq)))

(defn avg-damp-n
  [n]
  (apply comp (repeat n avg-damp)))

(defn sums
  [seq]
  (reductions + seq))

(defn Gregory-Leibniz-n
  [n]
  (/ (Math/pow -1 n) (inc (* 2 n))))

(def Gregory-Leibniz-pi
     (map #(* 4 (Gregory-Leibniz-n %)) (iterate inc 0)))

(println (first ((avg-damp-n 10) (sums Gregory-Leibniz-pi))))

Я получаю сообщение об ошибке «превышен предел накладных расходов gc» для n = 20. Как я могу это исправить?

ОБНОВЛЕНИЕ: я изменил функцию avg-damp-n

(defn avg-damp-n
  [n seq]
  (if (= n 0) seq
      (recur (dec n) (avg-damp seq))))

теперь я могу получить число для n=20

(time
 (let [n 20]
   (println n (first (avg-damp-n n (sums Gregory-Leibniz-pi))))))

20 3.141593197943081
"Elapsed time: 3705.821263 msecs"

ОБНОВЛЕНИЕ 2 Я исправил ошибку, и теперь все работает нормально:

(ns clojure101.series)

(defn avg [[x y]] (/ (+ x y) 2))

(defn avg-damp
  [seq]
  (map avg (partition 2 1 seq)))

(defn avg-damp-n
  [n]
  (apply comp (repeat n avg-damp)))

(defn sums
  [seq]
  (reductions + seq))

(defn Gregory-Leibniz-n
  [n]
  (/ (int (Math/pow -1 n)) (inc (* 2 n))))

(def Gregory-Leibniz-pi
     (map #(* 4 (Gregory-Leibniz-n %)) (range)))

; π = 3.14159265358979323846264338327950288419716939937510...

(time
 (let [n 100]
   (println n (double (first ((avg-damp-n n) (sums Gregory-Leibniz-pi)))))))

ВЫХОД:

100 3.141592653589793
"Elapsed time: 239.253227 msecs"

person edbond    schedule 17.10.2010    source источник


Ответы (3)


Хм... Это работает для меня. Протестировано с Clojure 1.2 в Windows XP.

user=> (defn avg
         [xs & {:keys [n] :or {n 2}}]
         (/ (reduce + xs) n))
#'user/avg
user=> (defn Gregory-Leibniz-n
         [n]
         (/ (Math/pow -1 n) (inc (+ n n))))
#'user/Gregory-Leibniz-n
user=> (->> (range)
         (map #(* 4 (Gregory-Leibniz-n %)))
         (reductions +)
         (partition 20)
         (map #(avg % :n 20))
         first
         println)
3.1689144018345354

Это правильный ответ? Я не знаю эту рекурсию Грегори-Лейбница, поэтому я не уверен, правильно ли это.

Один момент я отметил: вы пытаетесь быть слишком умным. А именно, ваш avg-damp-n складывает ленивые последовательности на ленивые последовательности. Поскольку вы можете добавлять произвольные значения n, вы рано или поздно столкнетесь с переполнением стека для больших n в таком сценарии. Если есть прямое решение, вы должны предпочесть его. Однако я не уверен, что это ваша реальная проблема. (Как я уже сказал: довольно бесполезно это работает для меня.)

person kotarak    schedule 18.10.2010
comment
Учитывая, что это должно разрешить число Пи, я не думаю, что это правильный ответ. ;) - person Alex Taggart; 18.10.2010
comment
@ataggert У тебя есть смысл. ;) (Небольшое редактирование: но это может быть число, которое ожидает ОП...) (И, конечно, с изменением (partition 2 1 ...) вышеизложенное больше не работает.) - person kotarak; 19.10.2010

Как заявил Котарак, наложение ленивых последовательностей на ленивые последовательности кажется довольно неэффективным в отношении GC. Я мог бы воспроизвести эту проблему в системе с медленным атомом. Смотрите также:

Ошибка java.lang.OutOfMemoryError: GC превышен предел накладных расходов

Для меня вычисление PI Грегори-Лейбница напрямую превращается в этот код Clojure, который использует только одну ленивую последовательность:

(defn Gregory-Leibniz-pi [n]
  (->> (range n)
       (map (fn [n] (/ (Math/pow -1 n) (inc (* 2 n)))))
       (apply +)
       (* 4)))
person Jürgen Hötzel    schedule 18.10.2010

Прежде всего, попробуйте глупое решение, которое работает: увеличьте пространство кучи java.

;in your clojure launch script
java -Xmx2G ...other options...

Есть одна часть программы, которая не ленится в партиции, но изменение ее так, чтобы она была ленивой (путем избавления от вызова count), по-прежнему дает мне OutOfMemoryError для размера кучи по умолчанию. Замена хитрости avg-damp-n вычисляемым средним значением на

(take (integer-exponent 2 20) seq)

по-прежнему вызывает ошибку OutOfMemoryError. Глядя на источник всего остального, я не вижу никаких других вещей, которые выглядят так, как будто они должны потреблять кучу.

person ellisbben    schedule 18.10.2010