1.2.18 #
解答 #
当数据比较大时—— 例如 10^9 加上随机小数组成的数列,这时 double 的小数精度将受限。
求和之后整数部分更大,小数部分将自动四舍五入,出现误差
这时再计算平均值时将会带来较大的误差。
因此采用另一个递推公式:
k 为下标。
$M_k = M_{k-1}+ (x_k – M_{k-1})/k$
$S_k = S_{k-1} + (x_k – M_{k-1})\times(x_k – M_k)$.
方差 $s^2 = S_k/(k – 1)$.
这种情况下并没有直接对所有输入值求和,小数精度不会过多受到整数部分长度的影响。
有关这两个公式的证明可以参考这篇论文,或者去查看我的知乎回答。
代码 #
public class Accumulator
{
private double _m;
private double _s;
private int _n;
public void AddDataValue(double x)
{
_n++;
_s = _s + 1.0 * (_n - 1) / _n * (x - _m) * (x - _m);
_m = _m + (x - _m) / _n;
}
public double Mean()
{
return _m;
}
public double Var()
{
return _s / (_n - 1);
}
public double Stddev()
{
return Math.Sqrt(Var());
}
public override string ToString()
{
return "Mean (" + _n + " values): " + string.Format("{0, 7:F5}", Mean());
}
}