1. Background Knowledge
The moving average filter (MAF) is a fundamental low-pass filter in signal processing that smooths data by replacing each value with the average of neighboring values in a fixed-size window of length k. For a 1D signal x=[x0,x1,…,xn−1], the output yi at position i is:
yi=k1j=i∑i+k−1xj,i=0,1,…,n−k
This produces n−k+1 values, reducing high-frequency noise while preserving low-frequency trends. In computer vision (CV), it's applied row/column-wise for image denoising before tasks like edge detection (e.g., Canny). Prerequisites: basic array indexing, summation, floating-point division, and rounding to 2 decimals (round(value, 2) in Python).
2. Algorithm Approach
Naive approach: For each i, sum k elements directly → O(nk) time, acceptable for n≤104, k≤n.
Optimized sliding window: Use a prefix sum array s=0, s[i+1]=s[i]+xi. Then yi=ks[i+k]−s[i]:
- Build prefix: O(n)
- Query each window: O(1)
- Total: O(n)
Recursive (cumulative average): Maintain running sum sum, update sum=sum−x[i−1]+x[i+k−1] → O(n) after O(k) init. Both optimizations beat naive for large k.
3. Step-by-Step Strategy
- Validate inputs: Ensure 1≤k≤n, n=len(signal).
- Choose method:
| Method | Pros | Cons | When to use |
|---|
| Naive sum | Simple, no edge cases | O(nk) slow for k ≈ n | k small (< 100) |
| Prefix sum | O(n), clean code | Extra O(n) space | General case |
| Recursive | O(n), O(1) space | Init overhead | Memory-tight |
- Compute averages: Generate list of length n−k+1.
- Format output: Convert to float, round to 2 decimals.
- Test edge cases: k=1 (identity), k=n (single avg).
Python prefix sum example:
def moving_average(signal, k):
n = len(signal)
if k > n or k < 1: raise ValueError("Invalid k")
prefix = * (n + 1)
for i in range(n):
prefix[i+1] = prefix[i] + signal[i]
return [round((prefix[i+k] - prefix[i]) / k, 2) for i in range(n - k + 1)]
4. Common Pitfalls
- Off-by-one indexing: Window ends at i+k−1, output length n−k+1 (not n).
- Integer division: Use float division (/k not //k); signal ints → avg float.
- Rounding: Apply round(avg, 2) after averaging, not per element.
- Edge handling: No padding specified → valid positions only (drop first/last incomplete windows).
- Empty/zero cases: n=1,k=1 → [signal]; negative values OK per constraints.
- Performance: Naive fails T.C. for n=104,k=104 (108 ops → timeout).
5. Time & Space Complexity
| Approach | Time | Space | Notes |
|---|
| Naive | O(nk) | O(n) output | 10^4 × 10^4 = 10^8 → borderline |
| Prefix/Recursive | O(n) | O(n) / O(1) extra | Optimal, scales perfectly |
| Output | - | O(n-k+1) = O(n) | Always required |
Best: Prefix sum for clarity, recursive for space. Both handle constraints efficiently.