假设我们有一个跟踪总和的基本移动平均函数。例如:
Queue values;
double sum;
double CalcSMA(double next) {
values.push(next);
sum -= values.pop();
sum += next;
return sum / SMA_LENGTH;
}
如果我们的窗口宽度为5,那么这可能会导致崩溃的一个例子就是:
2, 2, 2, 2, 2, 2, 2, 1E100, 2, 2, 2, 2, 2, 2, 2, 2
。
然后输出为2, 2, 2, 2E99, 2E99, 2E99, 2E99, 2E99, 0, 0, 0
。
即使总和并没有那么显着,但仍然可能有一些非常合理的例子,精确度的微小损失可能会使人为增加一小部分。在很长一段时间内,这可能会成为一个问题。
有没有人对如何解决精度损失有任何想法?
编辑:请注意,此功能旨在用于工作O(1)。这是必要的。因此,每次重新计算都不会起作用:窗口太大了。
答案 0 :(得分:4)
您可以重新计算每个SMA_LENGTH值的新总和,以阻止错误累积:
Queue values;
double sum = 0.0;
double freshsum = 0.0;
int count = 0;
double CalcSMA(double next) {
values.push(next);
sum -= values.pop();
sum += next;
freshsum += next;
if(++count == SMA_LENGTH)
{
sum = freshsum;
freshsum = 0.0;
count = 0;
}
return sum / SMA_LENGTH;
}
答案 1 :(得分:1)
如果你不断向它提供邪恶的价值,samgak提出的实际上并不能保证良好的平均值。
您可以使用Neumaier's算法在O(1)时间内生成准确的结果。像这样:
const double SMA_LENGTH = 5;
Queue values;
double sum = 0.0;
double correction = 0.0;
static void Neumaier(double value, ref double sum, ref double correction)
{
var t = sum + value;
if (Math.Abs(sum) >= Math.Abs(value))
correction += (sum - t) + value;
else
correction += (value - t) + sum;
sum = t;
}
double CalcSMA(double next)
{
Neumaier(-values.pop(), ref sum, ref correction);
Neumaier(next, ref sum, ref correction);
values.push(next);
return (sum + correction) / SMA_LENGTH;
}
如果您有大量序列,则可以每隔10 ^ 15左右重新添加窗口。这是因为,在双精度之后,算法在大约10 ^ 16次加法后开始失去准确性。
另一方面,Neumaier更复杂,所以它可能更慢。