
S. Doaitse Swierstra schrieb:
Avoiding repeated additions:
movingAverage :: Int -> [Float] -> [Float] movingAverage n l = runSums (sum . take n $l) l (drop n l) where n' = fromIntegral n runSums sum (h:hs) (t:ts) = sum / n' : runSums (sum-h+t) hs ts runSums _ _ [] = []
Moving average can be interpreted as convolution (*): [1/n,1/n,1/n,...,1/n] * xs You may drop the first (n-1) values, since they average over initial padding zeros. Convolution is associative and you can decompose the first operand into simpler parts: 1/n · [1,1,1,...] * [1,0,0,....,0,0,-1] * xs = 1/n · integrate ([1,0,0,....,0,0,-1] * xs) Convolution is commutative, thus you could also write = 1/n · [1,0,0,....,0,0,-1] * integrate xs but then integration of xs will yield unbounded values and thus higher rounding errors. This yields: movingAverage :: Int -> [Float] -> [Float] movingAverage n = drop (n-1) . map (/ fromIntegral n) . scanl1 (+) . (\xs -> zipWith (-) xs (replicate n 0 ++ xs)) This should be the same as the implementation above, but maybe a bit nicer. :-)