这是另一种方法,类似于Ed Heal建议的方法,它应该对舍入误差不太敏感。平均值的舍入误差随着累积和的大小而增长。这对您来说可能是也可能不是问题,但需要注意。
这是一个迭代算法,可以最小化平均舍入误差,这是我在Ross的旧版本(大约 1998 年)中第一次遇到的:
double Analyser::averageVolume()
{
double averageNumShares = 0.0;
for (int i = 0; i < nTransactions; i++)
{
double delta = (tArray[i].numShares - averageNumShares) / (i+1);
averageNumShares += delta;
}
return averageNumShares;
}
这是通过推导平均值的递归定义来实现的。也就是说,给定 samples x[1]
, ..., x[j]
, ..., x[N]
,您可以计算 sample 的第一个M+1
样本x[M+1]
的平均值和第一个样本的平均值M
:
sum(M) = x[1] + x[2] + ... + x[M]
thus avg(M+1) = sum(M+1)/(M+1) and avg(M) = sum(M)/M
avg(M+1) - avg(M) = sum(M+1)/(M+1) - sum(M)/M
= [ M*sum(M+1) - (M+1)*sum(M) ]/[ M * (M+1) ]
= [ M*(x[M+1] + sum(M)) - M*sum(M) - sum(M) ] / [ M*(M+1) ]
= [ M*x[M+1] - sum(M) ] / [ M*(M+1) ]
= [ x[M+1] - avg(M) ] / (M+1)
thus: avg(M+1) = avg(M) + [ x[M+1] - avg(M) ]/(M+1)
要了解这两种方法的舍入误差,请尝试计算 10^7 个样本的平均值,每个样本等于 1035.41。您最初的方法返回(在我的硬件上),平均为 1035.40999988683。上面的迭代方法返回 1035.41 的精确平均值。
不幸的是,两者在某些时候都是 O(N)。你原来的方案有 N 个加法和一个除法。迭代方案有 N 个加法、减法和除法,因此您为准确性付出了更多。