Idiomatic Windowing on Streams

Stuart Marks stuart.marks at oracle.com
Fri Oct 25 22:45:50 PDT 2013


On 10/25/13 9:34 PM, Stuart Marks wrote:
>           int[] sums = Arrays.copyOf(numbers, numbers.length);
>           Arrays.parallelPrefix(sums, Integer::sum);
>           int[] movsums = Arrays.copyOfRange(sums, N, sums.length);
>           Arrays.parallelSetAll(movsums, i -> movsums[i] - sums[i]);
>           double[] means = new double[movsums.length];
>           Arrays.parallelSetAll(means, i -> (double)movsums[i] / N);
>
> Note that my code snippet omits the first moving average instead of the last
> one, as in your example.
>
> Finally, you have to watch out for overflow, since the last element of the sums
> array will be the sum of all elements in the input array. It would be nice if
> there were a way to use a prefix operation to do the running sum and subtract
> off the element leaving the window, but I wasn't able to figure out how to do that.

Of course, right after I posted this I figured out how to do that. :-)

The trick is, from each element i, to first subtract off the (i-N)th element, 
and then to do the prefix sum. This gives the moving averages in-place. As a 
bonus, it saves a copy operation, and as another bonus, it computes all 
length-N+1 moving averages.

     int[] temp = Arrays.copyOf(numbers, numbers.length);
     Arrays.parallelSetAll(temp, i -> temp[i] - ((i < N) ? 0 : numbers[i - N]));
     Arrays.parallelPrefix(temp, Integer::sum);
     double[] means = new double[numbers.length];
     Arrays.parallelSetAll(means, i -> (double)temp[i] / N);

Elements [0..N-2] of the means array are garbage, but the remaining elements are 
the moving averages.

I have a feeling I just reinvented something FORTRAN programmers knew about in 
the 1960s.

s'marks


More information about the lambda-dev mailing list