(See the first and initial iteration.)
Now I make only one pass over the data in order to compute the standard deviation of the input Number
s, and that's how:
\begin{align} \sum_{i = 1}^n (x_i - \mu)^2 &= \sum_{i = 1}^n (x_i^2 - 2\mu x_i + \mu^2) \\ &= \sum_{i = 1}^n x_i^2 - 2\mu \sum_{i = 1}^n x_i + n \mu^2 \\ &= \sum_{i = 1}^n x_i^2 - 2 \Bigg( \frac{\sum_{i = 1}^n x_i}{n} \Bigg) \sum_{i = 1}^n x_i + n \Bigg( \frac{\sum_{i = 1}^n x_i}{n} \Bigg)^2 \\ &= \sum_{i = 1}^n x_i^2 - 2 \Bigg( \frac{\sum_{i = 1}^n x_i}{n} \Bigg) \sum_{i = 1}^n x_i + \frac{1}{n}\Bigg( \sum_{i = 1}^n x_i \Bigg)^2 \\ &= \sum_{i = 1}^n x_i^2 - \frac{2}{n} \Bigg( \sum_{i = 1}^n x_i \Bigg) \sum_{i = 1}^n x_i + \frac{1}{n}\Bigg( \sum_{i = 1}^n x_i \Bigg)^2 \\ &= \sum_{i = 1}^n x_i^2 - \frac{2}{n} \Bigg( \sum_{i = 1}^n x_i \Bigg)^2 + \frac{1}{n}\Bigg( \sum_{i = 1}^n x_i \Bigg)^2 \\ &= \sum_{i = 1}^n x_i^2 - \frac{1}{n} \Bigg( \sum_{i = 1}^n x_i \Bigg)^2. \end{align}
My code is as follows:
StandardDeviation.java:
package net.coderodde.util;
import java.util.Arrays;
import java.util.Collection;
import java.util.Objects;
import java.util.function.Consumer;
public final class StandardDeviation {
public static double computeStandardDeviation(final Number... array) {
Objects.requireNonNull(array, "The input number array is null.");
if (array.length == 0) {
return Double.NaN;
}
MyConsumer myConsumer = new MyConsumer();
Arrays.stream(array)
.map(Number::doubleValue)
.forEach(myConsumer);
int n = array.length;
double sum = myConsumer.getSum();
double sumOfSquares = myConsumer.getSumOfSquares();
double intermediate = sumOfSquares - sum * sum / n;
return Math.sqrt(intermediate / (n - 1));
}
public static double
computeStandardDeviation(final Collection<Number> collection) {
Objects.requireNonNull(collection,
"The input number collection is null");
return computeStandardDeviation(
collection.toArray(new Number[collection.size()]));
}
private StandardDeviation() {}
private static final class MyConsumer implements Consumer<Double> {
private double sum;
private double sumOfSquares;
@Override
public void accept(Double t) {
sum += t;
sumOfSquares += t * t;
}
double getSum() {
return sum;
}
double getSumOfSquares() {
return sumOfSquares;
}
}
public static void main(String[] args) {
// Mix 'em all!
double sd = computeStandardDeviation(Arrays.asList((byte) 1,
(short) 2,
3,
4L,
5.0f,
6.0));
System.out.println(sd);
}
}
Any critique much appreciated.