복붙노트

[SCALA] 목록의 이동 평균을 계산

SCALA

목록의 이동 평균을 계산

이번 주말은 좀 스칼라 및 Clojure에 나의 손을 시도하기로 결정했다. 나는 스칼라 언어로 데리러 쉽게,하지만 함수형 프로그래밍을 원했고 객체 지향 프로그래밍, 그리고 그래서 능숙 해요. 열심히 얻었다 곳이다.

난 그냥 쓰기 기능의 모드로 내 머리를 얻을 수없는 것. 전문가 기능 프로그래머로서, 당신은 어떻게 문제를 접근합니까?

값 목록 및 요약 정해진 기간을 감안할 때, 당신은 어떻게 목록의 이동 평균 간단한의 새 목록을 생성 할 것인가?

예를 들어 주어진리스트의 값 (2.0, 4.0, 7.0, 6.0, 3.0, 8.0, 12.0, 9.0, 4.0, 1.0)이고, 기간 (4), 함수 반환한다 (0.0, 0.0, 0.0, 4.75, 5.0, 6.0, 7.25, 8.0, 8.25, 6.5)

그것을 넘어서 심사숙고 하루에 지출 후, 최고의 나는 스칼라이이었다에 가지고 올 수 있습니다 :

def simpleMovingAverage(values: List[Double], period: Int): List[Double] = {
  (for (i <- 1 to values.length)
    yield
    if (i < period) 0.00
    else values.slice(i - period, i).reduceLeft(_ + _) / period).toList
}

나는 차라리 같은 일을 할 것,이 끔찍하게 비효율적 알고 :

where n < period: ma(n) = 0
where n = period: ma(n) = sum(value(1) to value(n)) / period
where n > period: man(n) = ma(n -1) - (value(n-period) / period) + (value(n) / period)

이제이 쉽게 필수적 스타일에서 수행 될 것이다,하지만 난 내 인생 기능적으로 그것을 표현하는 방법을 작동하지 않을 수 있습니다.

해결법

  1. ==============================

    1.재미있는 문제. 나는 효율성의 다양한 각도로 여러 솔루션을 생각할 수 있습니다. 반복적으로 물건을 추가 할 수있는 것은 정말 성능 문제가되지 않지만하자는 가정. 또한, 처음에 제로가 앞에 추가 할 수 있습니다 나중에, 그래서 그들을 생산에 대해 걱정하지 말자. 알고리즘이 자연을 제공하는 경우, 미세; 하지 않을 경우, 우리는 나중에 수정합니다.

    재미있는 문제. 나는 효율성의 다양한 각도로 여러 솔루션을 생각할 수 있습니다. 반복적으로 물건을 추가 할 수있는 것은 정말 성능 문제가되지 않지만하자는 가정. 또한, 처음에 제로가 앞에 추가 할 수 있습니다 나중에, 그래서 그들을 생산에 대해 걱정하지 말자. 알고리즘이 자연을 제공하는 경우, 미세; 하지 않을 경우, 우리는 나중에 수정합니다.

    스칼라 2.8을 시작으로, 다음은 목록의 슬라이딩 윈도우를 얻기 위해 슬라이딩을 사용하여 N> = 기간 동안의 결과를 줄 것이다 :

    def simpleMovingAverage(values: List[Double], period: Int): List[Double] =
      List.fill(period - 1)(0.0) ::: (values sliding period map (_.sum) map (_ / period))
    

    이 오히려 우아한하지만 이미 계산 추가를 활용하지 않기 때문에 그럼에도 불구하고, 그것은 최상의 성능을 가지고 있지 않습니다. 그래서, 그들에게 말하기, 우리는 그들을 어떻게받을 수 있나요?

    이제 우리는이 쓰기 가정 해 봅시다 :

    values sliding 2 map sum
    

    우리는 각각의 이쌍 합계의 목록을 가지고있다. 의 4 개 요소의 이동 평균을 계산하기 위해이 결과를 사용 해보자. 위의 수식은 다음 계산했다 :

    from d1, d2, d3, d4, d5, d6, ...
    to (d1+d2), (d2+d3), (d3+d4), (d4+d5), (d5+d6), ...
    

    우리는 각각의 요소를 가지고 두 번째 다음 요소에 추가 그래서, 우리는 4 개 요소의 이동 평균을 얻을 :

    (d1+d2)+(d3+d4), (d2+d3)+(d4+d5), (d3+d4)+(d5+d6), ...
    

    우리는이 같은 그것을 할 수 있습니다 :

    res zip (res drop 2) map Function.tupled(_+_)
    

    우리는 다음 8 개 요소의 이동 평균을 계산, 등등 수 있습니다. 음, 같은 패턴에 따라 컴퓨팅 가지로 잘 알려진 알고리즘이있다. 그것은 대부분의 다수의 컴퓨팅 파워에 그 사용에 대한 알려져있다. 그것은 다음과 같이 간다 :

    def power(n: Int, e: Int): Int = e match {
      case 0 => 1
      case 1 => n
      case 2 => n * n
      case odd if odd % 2 == 1 => power(n, (odd - 1)) * n
      case even => power(power(n, even / 2), 2)
    }
    

    자, 여기를 적용 할 수 있습니다 :

    def movingSum(values: List[Double], period: Int): List[Double] = period match {
      case 0 => throw new IllegalArgumentException
      case 1 => values
      case 2 => values sliding 2 map (_.sum)
      case odd if odd % 2 == 1 => 
        values zip movingSum(values drop 1, (odd - 1)) map Function.tupled(_+_)
      case even =>
        val half = even / 2
        val partialResult = movingSum(values, half)
        partialResult zip (partialResult drop half) map Function.tupled(_+_)
    }
    

    그래서, 여기에 논리입니다. 보다 크고, 그것을 짝수 또는 홀수 될 수 있으면 개시 0 무효이고, 기간 1은 입력과 동일하다, (2)는 기간 (2)의 크기의 윈도우를 슬라이딩한다.

    요소 - 이상한 경우, 우리는 다음 (1 홀수)의 movingSum에 각 요소를 추가 할 수 있습니다. 예를 들어, 3 경우에, 우리는 다음의 2 개 요소의 movingSum 각 요소를 추가한다.

    심지어 경우에, 우리는 N / 2 단계 이후에 하나의 각 요소를 추가, N / 2의 movingSum을 계산한다.

    그 정의, 우리는 다음 문제로 돌아가서이 작업을 수행 할 수 있습니다

    def simpleMovingAverage(values: List[Double], period: Int): List[Double] =
      List.fill(period - 1)(0.0) ::: (movingSum(values, period) map (_ / period))
    

    이 :::의 사용에 관해서는 약간의 비 효율성은, 그러나 그것의 O (기간)이 아닌 O (values.size). 그것은 꼬리 재귀 기능을보다 효율적으로 만들 수 있습니다. 그리고, 물론, 내가 제공 "슬라이딩"의 정의는 끔찍한 성능 현명하다,하지만 스칼라 2.8에 훨씬 더 나은 정의가 될 것입니다. 참고 우리는 목록에 효율적인 슬라이딩 방법을 만들 수 없다, 그러나 우리는의 Iterable에 그것을 할 수 있습니다.

    데 내가 중요한 경로 분석은 큰 문제로이 문제를 정확히 경우에만 매우 첫 번째 정의로 이동하고 최적화 것 모든 것을 말했다.

    결론적으로,이 전 문제에 대해 어떻게 됐는지 살펴 보자. 우리는 이동 평균 문제가 있습니다. 이동 평균은 윈도우의 크기로 나눈 목록 이동 "윈도우"의 합이다. 그래서, 첫째, 나는 그것에 슬라이딩 윈도우, 합계 모든 것을 얻으려고하고 크기에 의해 나눕니다.

    다음 문제는 이미 계산 된 추가의 반복을 피하기했다. 이 경우, 나는 작은 또한 가능한 가서 같은 결과를 재사용 더 큰 금액을 계산하는 방법을 알아 내기 위해 노력했다.

    마지막으로,의 추가 및 이전 결과에서 빼서 문제를 당신이 그것을 생각하는 방법을 해결하려고 할 수 있습니다. 최초의 평균을 얻는 것은 쉽다 :

     def movingAverage(values: List[Double], period: Int): List[Double] = {
       val first = (values take period).sum / period
    

    이제 우리는이 개 목록을 확인하십시오. 우선, 소자의리스트를 감산한다. 다음으로, 소자들의 목록에 추가한다 :

       val subtract = values map (_ / period)
       val add = subtract drop period
    

    우리는 지퍼를 사용하여 이러한 두 목록을 추가 할 수 있습니다. 작은 목록 빼기 필요한 것보다 더 큰 존재의 문제를 피할 수있는가이 방법은 많은 요소를 생성합니다 :

       val addAndSubtract = add zip subtract map Function.tupled(_ - _)
    

    우리는 배와 결과를 작성하여 마무리 :

       val res = (addAndSubtract.foldLeft(first :: List.fill(period - 1)(0.0)) { 
         (acc, add) => (add + acc.head) :: acc 
       }).reverse
    

    이는 반환에 대한 답변입니다. 전체 기능은 다음과 같습니다 :

     def movingAverage(values: List[Double], period: Int): List[Double] = {
       val first = (values take period).sum / period
       val subtract = values map (_ / period)
       val add = subtract drop period
       val addAndSubtract = add zip subtract map Function.tupled(_ - _)
       val res = (addAndSubtract.foldLeft(first :: List.fill(period - 1)(0.0)) { 
         (acc, add) => (add + acc.head) :: acc 
       }).reverse
       res
     }
    
  2. ==============================

    2.그래서 여기 간다, 더 나은 스칼라보다는 Clojure를 알고있다. 나는이 쓰고있는 여기에 다른 Clojure의 항목은 필수적이다; 그게 당신이 계신 정말로 (그리고 관용적 Clojure를하지 않습니다). 내 마음에 오는 첫 번째 알고리즘은 반복 시퀀스에서 요소의 요청을 추진하고 첫 번째 요소를 삭제하고, 반복된다.

    그래서 여기 간다, 더 나은 스칼라보다는 Clojure를 알고있다. 나는이 쓰고있는 여기에 다른 Clojure의 항목은 필수적이다; 그게 당신이 계신 정말로 (그리고 관용적 Clojure를하지 않습니다). 내 마음에 오는 첫 번째 알고리즘은 반복 시퀀스에서 요소의 요청을 추진하고 첫 번째 요소를 삭제하고, 반복된다.

    다음 순서 (게으른 여부를 벡터 또는 목록)의 모든 종류의 작품과는 무기한 크기의 목록 작업을하는 경우 도움이 될 수있는 평균의 게으른 순서를 --- 제공합니다. 이 소비 할 수있는 목록에 충분한 요소가없는 경우 암시 적으로 nil을 반환하여 기본 사건을 담당합니다.

    (defn moving-average [values period]
      (let [first (take period values)]
        (if (= (count first) period)
          (lazy-seq 
            (cons (/ (reduce + first) period)
                  (moving-average (rest values) period))))))
    

    테스트 데이터의 수율에 실행

    user> (moving-average '(2.0, 4.0, 7.0, 6.0, 3.0, 8.0, 12.0, 9.0, 4.0, 1.0) 4)
    (4.75 5.0 6.0 7.25 8.0 8.25 6.5)
    

    즉, 쉽게 (인위적으로 약간) 처리 할 수 ​​있지만 그것은 순서의 처음 몇 요소에 대해 "0"을 제공하지 않습니다.

    모두의 가장 쉬운 것은 패턴을 확인하고 법안을 맞는 사용 가능한 기능을 마음에 가져다 줄 수있을 것입니다. 파티션은 우리가 다음을 통해 매핑 할 수 있습니다 시퀀스의 부분의 게으른보기를 제공합니다 :

    (defn moving-average [values period]
      (map #(/ (reduce + %) period) (partition period 1 values))
    

    누군가가 꼬리 재귀 버전을 요청; 꼬리 재귀 대 게으름은 트레이드 오프의 비트입니다. 당신의 직업은 다음 목록을 구축하게되면 함수 꼬리 재귀는 일반적으로 매우 간단,이 단지 하위 기능에 인수로 목록을 구축 --- 예외는 아니다. 우리는 벡터 대신에, 그렇지 않으면 목록이 뒤쪽으로 구축되며, 마지막에 반전 될 필요가 있기 때문에 목록에 축적됩니다.

    (defn moving-average [values period]
      (loop [values values, period period, acc []]
        (let [first (take period values)]
          (if (= (count first) period)
            (recur (rest values) period (conj acc (/ (reduce + first) period)))
            acc))))
    

    루프는 (일종의 계획의 이름하자 등) 익명의 내부 기능을 만들 수있는 방법입니다; RECUR는 꼬리 호출을 제거하기 위해 Clojure에서 사용해야합니다. 접속사 컬렉션 ---리스트의 시작과 벡터의 끝을위한 방식으로 자연으로 추가 일반화 단점이다.

  3. ==============================

    3.여기에 또 다른 (기능)의 Clojure 용액이다 :

    여기에 또 다른 (기능)의 Clojure 용액이다 :

    (defn avarage [coll]
      (/ (reduce + coll)
         (count coll)))
    
    (defn ma [period coll]
      (map avarage (partition period 1 coll)))
    

    그 요구 사항 인 경우 시퀀스의 시작 부분의 제로는 여전히 추가해야합니다.

  4. ==============================

    4.여기 Clojure의에서 순수하게 기능적인 솔루션입니다. 더 이미 제공된 것보다 복잡하지만 게으른 만 대신 처음부터 다시 계산의 각 단계에서 평균을 조정합니다. 이 기간이 작은 경우의 각 단계에 새로운 평균을 계산하는 단순한 솔루션보다 실제로 더 느리다; 더 큰 기간 동안, 그러나, 오랜 기간 동안 더 수행 할 뭔가하고 (/ (테이크 기간 ...) 기간) 반면, 사실상 침체를 경험한다.

    여기 Clojure의에서 순수하게 기능적인 솔루션입니다. 더 이미 제공된 것보다 복잡하지만 게으른 만 대신 처음부터 다시 계산의 각 단계에서 평균을 조정합니다. 이 기간이 작은 경우의 각 단계에 새로운 평균을 계산하는 단순한 솔루션보다 실제로 더 느리다; 더 큰 기간 동안, 그러나, 오랜 기간 동안 더 수행 할 뭔가하고 (/ (테이크 기간 ...) 기간) 반면, 사실상 침체를 경험한다.

    (defn moving-average
      "Calculates the moving average of values with the given period.
      Returns a lazy seq, works with infinite input sequences.
      Does not include initial zeros in the output."
      [period values]
      (let [gen (fn gen [last-sum values-old values-new]
                  (if (empty? values-new)
                    nil
                    (let [num-out (first values-old)
                          num-in  (first values-new)
                          new-sum (+ last-sum (- num-out) num-in)]
                      (lazy-seq
                        (cons new-sum
                              (gen new-sum
                                   (next values-old)
                                   (next values-new)))))))]
        (if (< (count (take period values)) period)
          nil
          (map #(/ % period)
               (gen (apply + (take (dec period) values))
                    (cons 0 values)
                    (drop (dec period) values))))))
    
  5. ==============================

    5.여기에 부분적으로 포인트가없는 한 줄 하스켈 솔루션입니다 :

    여기에 부분적으로 포인트가없는 한 줄 하스켈 솔루션입니다 :

    ma p = reverse . map ((/ (fromIntegral p)) . sum . take p) . (drop p) . reverse . tails
    

    첫째는 그래서, "꼬리"얻을 목록에 목록에 꼬리를 적용 :

    Prelude List> tails [2.0, 4.0, 7.0, 6.0, 3.0]
    [[2.0,4.0,7.0,6.0,3.0],[4.0,7.0,6.0,3.0],[7.0,6.0,3.0],[6.0,3.0],[3.0],[]]
    

    반전을 삭제하고 상기 제 'P'항목 (여기서는 2로 피 복용)

    Prelude List> (drop 2 . reverse . tails) [2.0, 4.0, 7.0, 6.0, 3.0]
    [[6.0,3.0],[7.0,6.0,3.0],[4.0,7.0,6.0,3.0],[2.0,4.0,7.0,6.0,3.0]]
    

    경우는 (.)는 도트 / 유두 기호에 익숙하지 않은, 그것은 하나의 함수로 "를 구성하는"다른 사람의 입력으로 한 함수의 출력을 전달 의미 '기능성 조성물'에 대한 연산자이다. (g. F) ()에 X. g을 (F) 정도 "값의 런 (F) 다음 g에 출력을 전달"과 동일하다 (g (F (X))). 명확하게 프로그래밍 스타일에 일반적으로는 사용 리드.

    그런 다음, 매핑 함수 ((/ (fromIntegral P)). 합. P를 취할)리스트 상. 그래서 목록에있는 모든 목록을 그 다음에 'P'로 그들을 분할, 최초의 'P'요소 금액을합니다. 그리고 우리는 단지 "반대"로 다시 목록을 뒤집습니다.

    Prelude List> map ((/ (fromIntegral 2)) . sum . take 2) [[6.0,3.0],[7.0,6.0,3.0]
    ,[4.0,7.0,6.0,3.0],[2.0,4.0,7.0,6.0,3.0]]
    [4.5,6.5,5.5,3.0]
    

    이 모두는 그것보다 더 비효율적 많이 보이는; 목록이 평가 될 때까지 그냥 스택 (좋은 똑똑한 '게으른 하스켈)에 그것을 레이아웃, 물리적으로 목록의 순서를 반대로하지 않는다 "반전". "꼬리"또한 단지 참조를, 원래 목록의 다른 섹션을 모든 별도의 목록을 작성하지 않습니다. 그것은 여전히 ​​좋은 해결책은 아니지만, 한 줄 긴 :)

    여기에 슬라이딩 공제 및 추가를 할 mapAccum를 사용하는 약간 더 멋진하지만 더 이상 솔루션입니다 :

    ma p l = snd $ mapAccumL ma' a l'
        where
            (h, t) = splitAt p l
            a = sum h
            l' = (0, 0) : (zip l t)
            ma' s (x, y) = let s' = (s - x) + y in (s', s' / (fromIntegral p))
    

    처음에 우리는 그렇게, "P"두 부분으로 목록을 분할 :

    Prelude List> splitAt 2 [2.0, 4.0, 7.0, 6.0, 3.0]
    ([2.0,4.0],[7.0,6.0,3.0])
    

    첫 번째 비트를 합계 :

    Prelude List> sum [2.0, 4.0]
    6.0
    

    원래 목록과 제 2 비트 (두리스트로부터 순서대로 상품이 단지 오프 쌍) 지퍼. 원래 목록은 분명히 이상이지만, 우리는이 여분의 비트를 잃게 :

    Prelude List> zip [2.0, 4.0, 7.0, 6.0, 3.0] [7.0,6.0,3.0]
    [(2.0,7.0),(4.0,6.0),(7.0,3.0)]
    

    이제 우리는 우리의 mapAccum (ulator)에 대한 함수를 정의합니다. mapAccumL는 "지도"와 동일하지만 목록을 통해지도 실행과 다음 단계로 이전 "매핑"에서 전달되는 별도의 실행 상태 / 누적 매개 변수와 함께. 우리는 이동 평균으로 어큐뮬레이터를 사용하고 목록이 단지 슬라이딩 윈도우와 그냥 (목록 우리 단지 압축)에 입력되는 소자를 떠난 요소로 형성 될 때, 우리의 슬라이딩 기능은 제 번호 'X'소요 평균 거리에서 두 번째 숫자 'y'를 추가한다. 우리는 다음 새 '의'따라와 리턴 '의' 'P'로 나누어 전달합니다. mapAccumL 어큐뮬레이터뿐만 아니라 매핑리스트를 반환하므로 "SND"는 (초) 다만, mapAccumL의 제 2 복귀 값을 위해 사용되는 한 쌍 (튜플)의 제 2 부재 걸린다.

    은 $ 기호에 익숙하지 않은 당신의 사람들을 위해, 그것은 "응용 프로그램 운영자"입니다. 정말 아무것도하지 않고 당신이 브래킷 (테이크 노트 LISPers), 즉 (X f는) $ X F와 동일한을 떠날 수 있다는 것을 의미 그래서는이 "저, 오른쪽 연관 바인딩 우선 순위"가있다

    실행 (MA 4 2.0, 4.0, 7.0, 6.0, 3.0, 8.0, 12.0, 9.0, 4.0, 1.0]) 수율 [4.75, 5.0, 6.0, 7.25, 8.0, 8.25, 6.5] 중 솔루션.

    아, 그리고 당신도 솔루션을 컴파일 모듈 "목록"을 가져해야합니다.

  6. ==============================

    6.여기에 2 개 스칼라 2.8.0에서 이동 평균을 수행하는 방법 (엄격한 하나는 게으른가) 있습니다. 모두가 가정 대에서 적어도 P 복식에서

    여기에 2 개 스칼라 2.8.0에서 이동 평균을 수행하는 방법 (엄격한 하나는 게으른가) 있습니다. 모두가 가정 대에서 적어도 P 복식에서

    // strict moving average
    def sma(vs: List[Double], p: Int): List[Double] =
      ((vs.take(p).sum / p :: List.fill(p - 1)(0.0), vs) /: vs.drop(p)) {(a, v) =>
        ((a._1.head - a._2.head / p + v / p) :: a._1, a._2.tail)
      }._1.reverse
    
    // lazy moving average
    def lma(vs: Stream[Double], p: Int): Stream[Double] = {
      def _lma(a: => Double, vs1: Stream[Double], vs2: Stream[Double]): Stream[Double] = {
        val _a = a // caches value of a
        _a #:: _lma(_a - vs2.head / p + vs1.head / p, vs1.tail, vs2.tail)
      }
      Stream.fill(p - 1)(0.0) #::: _lma(vs.take(p).sum / p, vs.drop(p), vs)
    }
    
    scala> sma(List(2.0, 4.0, 7.0, 6.0, 3.0, 8.0, 12.0, 9.0, 4.0, 1.0), 4)
    res29: List[Double] = List(0.0, 0.0, 0.0, 4.75, 5.0, 6.0, 7.25, 8.0, 8.25, 6.5)
    
    scala> lma(Stream(2.0, 4.0, 7.0, 6.0, 3.0, 8.0, 12.0, 9.0, 4.0, 1.0), 4).take(10).force
    res30: scala.collection.immutable.Stream[Double] = Stream(0.0, 0.0, 0.0, 4.75, 5.0, 6.0, 7.25, 8.0, 8.25, 6.5)
    
  7. ==============================

    7.J 프로그래밍 언어는 이동 평균 등의 프로그램을 용이하게합니다. 사실, 자신의 레이블에 비해 (+ / % 번호) \, 적은 문자가 '이동 평균은.'

    J 프로그래밍 언어는 이동 평균 등의 프로그램을 용이하게합니다. 사실, 자신의 레이블에 비해 (+ / % 번호) \, 적은 문자가 '이동 평균은.'

    (이름이 '가치'포함)이 질문에 지정된 값은 여기 코드이에 대한 간단한 방법입니다 :

       values=: 2 4 7 6 3 8 12 9 4 1
       4 (+/ % #)\ values
    4.75 5 6 7.25 8 8.25 6.5
    

    우리는 구성 요소 레이블을 사용하여 설명 할 수 있습니다.

       periods=: 4
       average=: +/ % #
       moving=: \
    
       periods average moving values
    4.75 5 6 7.25 8 8.25 6.5
    

    두 예제는 정확히 같은 프로그램을 사용합니다. 유일한 차이점은 제 형태로 더 이름을 사용한다. 이러한 이름은 J의 예비 선거를 모르는 독자들에게 도움이 될 수 있습니다.

    의 평균, 서브 프로그램에서 무슨 일이 일어나고 있는지에 조금 더를 살펴 보자. + / 나타내고, 합계 (Σ) 및 %는 (고전 ÷ 기호 등) 부문이다. 항목의 집계 (수를) 계산하는 번호로 이루어집니다. + / % 번호 : 전체 프로그램, 그리고, 값의 집계로 나눈 값의 합

    여기에 기록 된 이동 평균 계산의 결과는 원래의 질문에 예상 앞에 0을 포함하지 않습니다. 그 제로는 틀림없이 의도 된 계산에 포함되지 않습니다.

    여기에 사용 된 기술은 암묵적 프로그램이라고합니다. 그것은 꽤 많은 함수형 프로그래밍의 지점이없는 스타일과 동일합니다.

  8. ==============================

    8.여기 Clojure의는 더 많은 기능 언어 척. 이 BTW, 완전히 꼬리 재귀이며, 선행 0이 포함되어 있습니다.

    여기 Clojure의는 더 많은 기능 언어 척. 이 BTW, 완전히 꼬리 재귀이며, 선행 0이 포함되어 있습니다.

    (defn moving-average [period values]
      (loop [[x & xs]  values
             window    []
             ys        []]
    
        (if (and (nil? x) (nil? xs))
          ;; base case
          ys
    
          ;; inductive case
          (if (< (count window) (dec period))
            (recur xs (conj window x) (conj ys 0.0))
            (recur xs
                   (conj (vec (rest window)) x)
                   (conj ys (/ (reduce + x window) period)))))))
    
    (deftest test-moving-average
      (is (= [0.0 0.0 0.0 4.75 5.0 6.0 7.25 8.0 8.25 6.5]
             (moving-average 4 [2.0 4.0 7.0 6.0 3.0 8.0 12.0 9.0 4.0 1.0]))))
    

    보통 나는 카레하는 기능을 쉽게하기 위해 마지막 매개 변수 컬렉션 또는 목록을 넣어. 그러나 Clojure의에서 ...

    (partial moving-average 4)
    

    ... 성가신, 나는 일반적으로이 일을 끝낼 수 있도록입니다 ...

    #(moving-average 4 %)
    

    ...이 경우, 정말 매개 변수가 가서 주문 중요하지 않습니다.

  9. ==============================

    9.다음은 Clojure의 버전입니다 :

    다음은 Clojure의 버전입니다 :

    때문에 게으른 서열, 그것은 완벽하게 일반의 및 스택을 날려하지 않습니다

    (defn partialsums [start lst]
      (lazy-seq
        (if-let [lst (seq lst)] 
              (cons start (partialsums (+ start (first lst)) (rest lst)))
              (list start))))
    
    (defn sliding-window-moving-average [window lst]
      (map #(/ % window)
           (let [start   (apply + (take window lst))
                 diffseq (map   - (drop window lst) lst)]
             (partialsums start diffseq))))
    

    ;; 도움에 어떤 일이 일어나는지 확인

    (sliding-window-moving-average 5 '(1 2 3 4 5 6 7 8 9 10 11))
    
    start = (+ 1 2 3 4 5) = 15
    
    diffseq = - (6 7 8 9 10 11)
                (1 2 3 4  5  6 7 8 9 10 11)
    
            =   (5 5 5 5  5  5)
    
    (partialsums 15 '(5 5 5 5 5 5) ) = (15 20 25 30 35 40 45)
    
    (map #(/ % 5) (20 25 30 35 40 45)) = (3 4 5 6 7 8 9)
    

    ;; 예

    (take 20 (sliding-window-moving-average 5 (iterate inc 0)))
    
  10. ==============================

    10.나에게이 사건의 실용적인 솔루션과 윈도 평균 함수를 작성하는 폐쇄 이후이 예는, 상태를 사용합니다 :

    나에게이 사건의 실용적인 솔루션과 윈도 평균 함수를 작성하는 폐쇄 이후이 예는, 상태를 사용합니다 :

    (defn make-averager [#^Integer period]
      (let [buff (atom (vec (repeat period nil)))
            pos (atom 0)]
        (fn [nextval]
          (reset! buff (assoc @buff @pos nextval))
          (reset! pos (mod (+ 1 @pos) period))
          (if (some nil? @buff)
            0
            (/ (reduce + @buff)
               (count @buff))))))
    
    (map (make-averager 4)
         [2.0, 4.0, 7.0, 6.0, 3.0, 8.0, 12.0, 9.0, 4.0, 1.0])
    ;; yields =>
    (0 0 0 4.75 5.0 6.0 7.25 8.0 8.25 6.5)
    

    이 부작용없는 것은 아니지만 그것은 여전히 ​​최고 수준의 기능을 활용의 의미에서 작동합니다. 두 언어 당신은 JVM의 상단에 모두 실행을 언급하고 필요한 경우 따라서 두 국가 관리 할 수 ​​있습니다.

  11. ==============================

    11.이 솔루션은 나에게 더 잘 알고있는, 하스켈에 있습니다 :

    이 솔루션은 나에게 더 잘 알고있는, 하스켈에 있습니다 :

    slidingSums :: Num t => Int -> [t] -> [t]
    slidingSums n list = case (splitAt (n - 1) list) of
                          (window, []) -> [] -- list contains less than n elements
                          (window, rest) -> slidingSums' list rest (sum window)
      where
        slidingSums' _ [] _ = []
        slidingSums' (hl : tl) (hr : tr) sumLastNm1 = sumLastN : slidingSums' tl tr (sumLastN - hl)
          where sumLastN = sumLastNm1 + hr
    
    movingAverage :: Fractional t => Int -> [t] -> [t]
    movingAverage n list = map (/ (fromIntegral n)) (slidingSums n list)
    
    paddedMovingAverage :: Fractional t => Int -> [t] -> [t]
    paddedMovingAverage n list = replicate (n - 1) 0 ++ movingAverage n list
    

    스칼라 번역 :

    def slidingSums1(list: List[Double], rest: List[Double], n: Int, sumLastNm1: Double): List[Double] = rest match {
        case Nil => Nil
        case hr :: tr => {
            val sumLastN = sumLastNm1 + hr
            sumLastN :: slidingSums1(list.tail, tr, n, sumLastN - list.head)
        }
    }
    
    def slidingSums(list: List[Double], n: Int): List[Double] = list.splitAt(n - 1) match {
        case (_, Nil) => Nil
        case (firstNm1, rest) => slidingSums1(list, rest, n, firstNm1.reduceLeft(_ + _))
    }
    
    def movingAverage(list: List[Double], n: Int): List[Double] = slidingSums(list, n).map(_ / n)
    
    def paddedMovingAverage(list: List[Double], n: Int): List[Double] = List.make(n - 1, 0.0) ++ movingAverage(list, n)
    
  12. ==============================

    12.에 관계없이 기간 O (목록 길이)되는 장점을 가지고 짧은 Clojure의 버전 :

    에 관계없이 기간 O (목록 길이)되는 장점을 가지고 짧은 Clojure의 버전 :

    (defn moving-average [list period]
      (let [accums (let [acc (atom 0)] (map #(do (reset! acc (+ @acc %1 ))) (cons 0 list)))
            zeros (repeat (dec period) 0)]
         (concat zeros (map #(/ (- %1 %2) period) (drop period accums) accums))))
    

    하여 두 숫자를하고 감산 - 이것은 시퀀스의 누적 합을 생성 (> [0 1 3 6 10 15] 예를 들어, [1 2 3 4 5])에 의해 숫자 범위의 합계를 산출 할 수 있다는 사실을 이용한다 은 기간과 동일한 오프셋.

  13. ==============================

    13.(:이 실제로 이동 평균을 나타내는 적절한 방법이 아니므로 값을 0.0으로 처음 3 개 요소는 반환되지 않습니다 주) 내가 파이썬에서 그것을 할 것입니다 방법을 알고있다. 나는 유사한 기술은 스칼라에서 실현 될 것입니다 상상에 맡기겠습니다. 여기에 그것을 할 수있는 여러 가지 방법이 있습니다.

    (:이 실제로 이동 평균을 나타내는 적절한 방법이 아니므로 값을 0.0으로 처음 3 개 요소는 반환되지 않습니다 주) 내가 파이썬에서 그것을 할 것입니다 방법을 알고있다. 나는 유사한 기술은 스칼라에서 실현 될 것입니다 상상에 맡기겠습니다. 여기에 그것을 할 수있는 여러 가지 방법이 있습니다.

    data = (2.0, 4.0, 7.0, 6.0, 3.0, 8.0, 12.0, 9.0, 4.0, 1.0)
    terms = 4
    expected = (4.75, 5.0, 6.0, 7.25, 8.0, 8.25, 6.5)
    
    # Method 1 : Simple. Uses slices
    assert expected == \
        tuple((sum(data[i:i+terms])/terms for i in range(len(data)-terms+1)))
    
    # Method 2 : Tracks slots each of terms elements
    # Note: slot, and block mean the same thing.
    # Block is the internal tracking deque, slot is the final output
    from collections import deque
    def slots(data, terms):
        block = deque()
        for datum in data :
            block.append(datum)
            if len(block) > terms : block.popleft()
            if len(block) == terms :
                yield block
    
    assert expected == \
        tuple(sum(slot)/terms for slot in slots(data, terms))
    
    # Method 3 : Reads value one at a time, computes the sums and throws away read values
    def moving_average((avgs, sums),val):
        sums = tuple((sum + val) for sum in sums)
        return (avgs + ((sums[0] / terms),), sums[1:] + (val,))
    
    assert expected == reduce(
        moving_average,
        tuple(data[terms-1:]),
        ((),tuple(sum(data[i:terms-1]) for i in range(terms-1))))[0]
    
    # Method 4 : Semantically same as method 3, intentionally obfuscates just to fit in a lambda
    assert expected == \
        reduce(
            lambda (avgs, sums),val: tuple((avgs + ((nsum[0] / terms),), nsum[1:] + (val,)) \
                                    for nsum in (tuple((sum + val) for sum in sums),))[0], \
               tuple(data[terms-1:]),
               ((),tuple(sum(data[i:terms-1]) for i in range(terms-1))))[0]
    
  14. ==============================

    14.재귀 솔루션을 찾고있는 것 같습니다. 이 경우, 나는 약간 문제를 변경 제안하고 점점 목표 (4.75, 5.0, 6.0, 7.25, 8.0, 8.25, 6.5, 0.0, 0.0, 0.0) 솔루션으로.

    재귀 솔루션을 찾고있는 것 같습니다. 이 경우, 나는 약간 문제를 변경 제안하고 점점 목표 (4.75, 5.0, 6.0, 7.25, 8.0, 8.25, 6.5, 0.0, 0.0, 0.0) 솔루션으로.

    이 경우에는 스칼라에서 아래 우아한 재귀 솔루션을 쓸 수 있습니다 :

    def mavg(values: List[Double], period: Int): List[Double] = {
      if (values.size < period) List.fill(values.size)(0.0) else
        if (values.size == period) (values.sum / values.size) :: List.fill(period - 1)(0.0) else {
          val rest: List[Double] = mavg(values.tail, period)
          (rest.head + ((values.head - values(period))/period)):: rest
      }
    }
    
  15. ==============================

    15.너무 파티에 지각 및 기능 프로그래밍 새, 나는 내부 기능이 솔루션에 온 :

    너무 파티에 지각 및 기능 프로그래밍 새, 나는 내부 기능이 솔루션에 온 :

    def slidingAvg (ixs: List [Double], len: Int) = {
        val dxs = ixs.map (_ / len) 
        val start = (0.0 /: dxs.take (len)) (_ + _)
        val head = List.make (len - 1, 0.0)
    
        def addAndSub (sofar: Double, from: Int, to: Int) : List [Double] =  
            if (to >= dxs.length) Nil else {
                val current = sofar - dxs (from) + dxs (to) 
                current :: addAndSub (current, from + 1, to + 1) 
            }
    
        head ::: start :: addAndSub (start, 0, len)
    }
    
    val xs = List(2, 4, 7, 6, 3, 8, 12, 9, 4, 1)
    slidingAvg (xs.map (1.0 * _), 4)
    

    나는 사전에 (렌) 기간에 의해 전체 목록을 분할하는 아이디어를 채택했다. 그럼 난 렌 - 첫 번째 요소로 시작하는 합계를 생성합니다. 그리고, I는 (0.0, 0.0, ...) 제 무효 요소를 생성한다.

    그럼 재귀 첫 번째 빼고 마지막 값을 추가합니다. 결국 나는 전체를 listify.

  16. ==============================

    16.하스켈 의사 코드에서 :

    하스켈 의사 코드에서 :

    group4 (a:b:c:d:xs) = [a,b,c,d] : group4 (b:c:d:xs)
    group4 _ = []
    
    avg4 xs = sum xs / 4
    
    running4avg nums = (map avg4 (group4 nums))
    

    또는 pointfree

    runnig4avg = map avg4 . group4
    

    (이제 하나 정말해야 추상화 4 점 ....)

  17. ==============================

    17.하스켈 사용 :

    하스켈 사용 :

    movingAverage :: Int -> [Double] -> [Double]
    movingAverage n xs = catMaybes . (fmap avg . take n) . tails $ xs
      where avg list = case (length list == n) -> Just . (/ (fromIntegral n)) . (foldl (+) 0) $ list
                            _                  -> Nothing
    

    주요 결과의 n 번째 요소는 상기 제 n-1 개의 요소가 없기 때문에 특성과 원래 목록 사본 목록을리스트를 매핑하는 꼬리 함수이다.

    그래서

    [1,2,3,4,5] -> [[1,2,3,4,5], [2,3,4,5], [3,4,5], [4,5], [5], []]
    

    우리는 fmap 함수 (평균을. N 소요) 적용 우리가 하위 목록에서 N-길이 접두어을 의미하는 결과로, 그 평균을 계산합니다. 우리가 avg'ing되는리스트의 길이가 N이 아닌 경우 (이 정의되지 않기 때문에), 우리는 평균 계산하지 않습니다. 이 경우, 우리는 아무것도를 반환합니다. 이 경우, 우리가 할, 그리고 "그냥"에 포장. 마지막으로, 우리는 fmap 함수의 결과에 "catMaybes"실행 (평균을. N 소요) 어쩌면 입력 없애.

  18. ==============================

    18.나는 @JamesCunningham의 게으른 서열 솔루션 가장 관용적 Clojure의 솔루션을 나에게 보인 무슨의 성능에 실망 (놀라게)이었다.

    나는 @JamesCunningham의 게으른 서열 솔루션 가장 관용적 Clojure의 솔루션을 나에게 보인 무슨의 성능에 실망 (놀라게)이었다.

    (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"
    

    그래서 여기에 이동 금액에 빠르게 지수를 적응의 생각 '@ DanielC.Sobral와 솔루션'제임스의 조합이다 :

    (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"
    

    편집 : @mikera의 솔루션 -에 기반이 하나가 더 빨리이다.

    (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"
    
  19. from https://stackoverflow.com/questions/1319891/calculating-the-moving-average-of-a-list by cc-by-sa and MIT license