Calculating the Moving Average of a List

后端 未结 18 1136
悲哀的现实
悲哀的现实 2020-12-07 09:01

This weekend I decided to try my hand at some Scala and Clojure. I\'m proficient with object oriented programming, and so Scala was easy to pick up as a language, but wante

18条回答
  •  庸人自扰
    2020-12-07 09:44

    I was (surprised and) disappointed by the performance of what seemed to me the most idiomatic Clojure solutions, @JamesCunningham 's lazy-seq solutions.

    (def integers (iterate inc 0))
    (def coll (take 10000 integers))
    (def n 1000)
    (time (doall (moving-average-james-1 coll n)))
    # "Elapsed time: 3022.862 msecs"
    (time (doall (moving-average-james-2 coll n)))
    # "Elapsed time: 3433.988 msecs"
    

    So here's a combination of James' solution with @DanielC.Sobral 's idea of adapting fast-exponentiation to moving sums :

    (defn moving-average
      [coll n]
      (letfn [(moving-sum [coll n]
                (lazy-seq
                  (cond
                    (= n 1)  coll
                    (= n 2)  (map + coll (rest coll))
                    (odd? n) (map + coll (moving-sum (rest coll) (dec n)))
                    :else    (let [half (quot n 2)
                                   hcol (moving-sum coll half)]
                               (map + hcol (drop half hcol))))))]
        (cond
          (< n 1) nil
          (= n 1) coll
          :else   (map #(/ % n) (moving-sum coll n)))))
    
    
    (time (doall (moving-average coll n)))
    # "Elapsed time: 42.034 msecs"
    

    Edit: this one -based on @mikera 's solution- is even faster.

    (defn moving-average
      [coll n]
      (cond
        (< n 1) nil
        (= n 1) coll
        :else   (let [sums (reductions + 0 coll)]
                  (map #(/ (- %1 %2) n) (drop n sums) sums))))
    
    (time (doall (moving-average coll n)))
    # "Elapsed time: 9.184 msecs"
    

提交回复
热议问题