【发布时间】:2014-05-11 06:22:04
【问题描述】:
我是 Clojure 的新手。我想知道如何优化算法来计算列表中的反转次数。据我了解,除非特别要求,否则 Clojure 不会进行尾调用优化?你如何让它做到这一点?
第一次尝试使用变异变量的运行时间约为 3.5 秒。但我的第二次尝试是功能版本,大约需要 1m15s!并且两者都需要增加相当多的堆栈大小(例如-Xss12m)。
我将如何获得更好的性能?
如果可能的话,我宁愿没有可变变量(比如函数变量)。您可以通过键入 seq 100000 | sort -R > IntArray.txt 之类的内容来创建数组文件。
第一次尝试使用可变变量:
(use 'clojure.java.io)
(def inversions 0)
(defn merge_and_count' [left right left_len]
(if (empty? right) left
(if (empty? left) right
(if (<= (first left) (first right))
(cons (first left) (merge_and_count' (rest left) right (- left_len 1)))
(let [_ (def inversions (+ inversions left_len))]
(cons (first right) (merge_and_count' left (rest right) left_len)))
))))
(defn inversion_count [list]
(if (or (empty? list) (nil? (next list))) list
(let [mid (quot (count list) 2)]
(merge_and_count' (inversion_count (take mid list))
(inversion_count (drop mid list)) mid)
)))
(defn parse-int [s]
(Integer. (re-find #"\d+" s )))
(defn get-lines [fname]
(with-open [r (reader fname)]
(doall (map parse-int (line-seq r)))))
(let [list (get-lines "IntArray.txt")
_ (inversion_count list)]
(print inversions))
我第二次尝试纯函数式(无可变性):
(use 'clojure.java.io)
(defn merge_and_count' [left right inversions]
(if (empty? right) (list left inversions)
(if (empty? left) (list right inversions)
(if (<= (first left) (first right))
(let [result (merge_and_count' (rest left) right inversions)]
(list (cons (first left) (first result)) (second result)))
(let [result (merge_and_count' left (rest right) (+ inversions (count left)))]
(list (cons (first right) (first result)) (second result)))
))))
(defn inversion_count [list' list_len]
(if (or (empty? list') (nil? (next list'))) (list list' 0)
(let [mid (quot list_len 2)
left (inversion_count (take mid list') mid)
right (inversion_count (drop mid list') (- list_len mid))]
(merge_and_count' (first left) (first right) (+ (second left) (second right)))
)))
(defn parse-int [s]
(Integer. (re-find #"\d+" s )))
(defn get-lines [fname]
(with-open [r (reader fname)]
(doall (map parse-int (line-seq r)))))
(let [list (get-lines "IntArray.txt")
result (inversion_count list 100000)]
(print (second result)))
【问题讨论】:
-
这个算法应该做什么?我只看到一大堆
first、rest和second。 -
@amalloy 参见here,例如
-
@pete23 他的递归调用不在尾部位置,因此用
recur替换它们将用编译器错误替换糟糕的性能。 -
我假设我们正在寻求解决这个问题? stackoverflow.com/questions/337664/… 即列表中每个项目的总和,即小于它的后继条目的#。 n^2 解决方案应该是微不足道的。
-
Loop/recur 并不完全是“如何在 Clojure 中启用尾调用优化”——Clojure 中没有尾调用优化。但是循环/递归是提供类似功能的替代方案。文档在这里:clojure.org/special_forms#Special%20Forms--(recur%20exprs*)
标签: optimization clojure mutable