如何高效地计算流动平均值(移动平均值)?

68

我想出了这个想法

n=1;
curAvg = 0;
loop{
  curAvg = curAvg + (newNum - curAvg)/n;
  n++;
}

我认为这种方式的亮点如下:
- 它避免了大数字(如果您将它们加起来再除以数量会可能导致溢出)
- 您可以节省一个寄存器(不需要存储总和)

问题可能出在求和误差上 - 但是我假设整体上应该有近似相等数量的四舍五入,所以误差不应该累积得太多。

您是否看到这种解决方案中存在任何陷阱? 您有更好的建议吗?


我不理解你的公式。对于1 23,接下来你会做curAvg = 1.5 + (3 - 1.5)/2 = 1.5 + 0.75 = 2.25,这是错误的吗? - IVlad
1
类似问题:https://dev59.com/VGcs5IYBdhLWcg3wtmMD - Donald_W
@IVlad:循环1:curAvg=0+(1-0)/1=1;n=2<br>循环2:curAvg=1+(2-1)/2=1.5;n=3<br>循环3:curAvg=1.5+(3-1.5)/3=2;n=4 - Vit Bernatik
3
你的解决方案已经在那里提到:新平均值 = 旧平均值 + (下一个数据 - 旧平均值) / 下一个计数 - Donald_W
1
@IVlad,您忘记增加n的值了。 它应该是3而不是2。所以表达式应该是curAvg = 1.5 + (3-1.5) / 3 = 1.5 + 0.5 = 2,这是正确的。 - Natesh Raina
4
需要注意的是,OP的算法不是标准移动平均线,而是指数加权移动平均线。虽然在许多应用中EMA可能是最佳选择,但在某些情况下(大步响应)两者的行为差异很大,实现者应该意识到这种差异。请参见https://dev59.com/VGcs5IYBdhLWcg3wtmMD。 - Julia
3个回答

36

您的解决方案本质上是保持平均值的标准最佳在线解决方案,而无需存储大量总和,同时“在线”运行,即每次只需处理一个数字,而无需回到其他数字,并且仅使用恒定数量的额外内存。如果您想获得稍微优化的数值精度解决方案,以“在线”的代价为前提,那么假设您的数字都是非负的,则首先将数字按从小到大排序,然后按照相同的方式进行处理。这样,如果您获得一堆大约相等的很小的数字,然后获得一个大数字,您将能够在不会下溢的情况下准确计算平均值,而不是处理大数字的情况下。


7

我已经使用这个算法很多年了。 循环可以是任何类型的循环。可能是单个Web会话,也可能是真实的循环。重点是只需要跟踪当前计数(N)和当前平均值(avg)。每次接收到一个新值时,应用此算法更新平均值。这将计算精确的算术平均值。它具有额外的好处,即具有抗溢出性。如果你要对成千上万个大数求平均值,在进行除以N之前将它们全部相加可能会导致溢出。该算法避免了这种陷阱。

Variables that are stored during the computation of the average:
N = 0
avg = 0

For each new value: V
    N=N+1
    a = 1/N
    b = 1 - a
    avg = a * V + b * avg

0
这更像是一个总体平均值,而不是移动平均值。移动平均值仅计算最近几个输入数字的平均值,比如最近的5个数字。一旦第六个数字进来,就必须将第一个数字排除在外,以此类推。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接