(defun MOVING-AVERAGE (x width)
"Smooths assuming input x is equally spaced."
(unless (oddp width)
(format t "Width needs to be odd integer >= 3.~%")
(setf width (let ((rw (round width)) )
(if (oddp rw) rw (1+ rw)))) )
(let* ((half (/ (1- width) 2)) ; invariants done once
(n (length x))
(count half)
(sum (sum (select x (iseq half)))) ; initialize terms
(drop (- (1+ half)))
(add half)
(smth ()) )
(dotimes (i n)
(when (<= 0 drop)
(decf sum (select x drop))
(decf count))
(when (< add n)
(incf sum (select x add))
(incf count))
(incf drop)
(incf add)
; (format t "~d ~d count=~d~%" drop add count)
(push (/ sum count) smth))
(nreverse smth)))
The speed of the calculation does not depend on the width of the smoother with
this approach. However, as we have made it faster, we have also made it
more specialized. This version does not generalize to weighted averages so simply
as the prior version. We have built the constant smoothing weights into the
algorithm, replacing the call to mean by the decf's and incf's.
In this form, animation works rather nicely as long as the series to be smoothed is no more than 100-200 points.
void MovAvg (double x[], long *n, long *w, double smth[])
{
long i, half, count, drop, add;
double sum;
count = half = (*w - 1)/2;
sum = 0.0;
for (i=0; i < half; ++i)
sum += x[i];
drop = - (1+half);
add = half;
for (i=0; i< *n; ++i)
{ if (drop >= 0)
{ sum -= x[drop];
--count;
}
if (add < *n)
{ sum += x[add];
++count;
}
smth[i] = sum / count;
++drop;
++add;
}
}
In order to use this function, we first compile it into an "object file"
movAvg.o using the Unix cc command:
cc -c +O3 -Aa +Z movAvg.c
The man pages describe these options which
ld -o movAvg.shlb -b movAvg.o
Now we can use dyn-load to read the shared library, and then use
call-cfun to make things happen.