我是Clojure的新手。我想知道如何优化算法来计算列表中的反转次数。根据我的理解,除非特别要求,否则Clojure不进行尾调优化?你怎么做到这一点?
使用变异变量的第一次尝试具有大约 3.5s 的运行时间。但我的第二次尝试是一个功能版本,它需要 1m15s !并且都需要增加堆栈大小(如-Xss12m
)。
我如何才能获得更好的表现?
如果可能的话,我更喜欢没有可变变量(如功能变量)。您可以通过键入seq 100000 | sort -R > IntArray.txt
。
第一次尝试w / mutable变量:
(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)))
答案 0 :(得分:3)
由于merge-and-count中的递归,堆栈溢出。我尝试了这种方法,对于100000件物品,它立即回来了。
(defn merge_and_count [left right inversions]
(loop [l left r right inv inversions result []]
(cond (and (empty? r) (empty? l)) [result inv]
(empty? r) [(apply conj result l) inv]
(empty? l) [(apply conj result r) inv]
(<= (first l) (first r)) (recur (rest l) r inv (conj result (first l)))
:else (recur l (rest r) (+ inv (count l)) (conj result (first r))))))
您需要使用第二种方法中的代码替换此代码。