假设我想计算数据集的平均值,例如
class Averager {
float total;
size_t count;
float addData (float value) {
this->total += value;
return this->total / ++this->count;
}
}
total
或count
值迟早会溢出,所以我不记得总价值:
class Averager {
float currentAverage;
size_t count;
float addData (float value) {
this->currentAverage = (this->currentAverage*count + value) / ++count;
return this->currentAverage;
}
}
它们似乎会溢出更长时间,但average
和count
之间的乘法导致溢出问题,因此下一个解决方案是:
class Averager {
float currentAverage;
size_t count;
float addData (float value) {
this->currentAverage += (value - this->currentAverage) / ++count;
return this->currentAverage;
}
}
似乎更好,下一个问题是如何防止count
溢出?
答案 0 :(得分:7)
聚合桶。
我们选择的铲斗尺寸比squareRoot(MAXINT)小得多。为了简单起见,我们选择10。
每个新值都会添加到当前存储桶中,并且可以按照您的描述计算移动平均值。
当存储桶已满时,启动一个新存储桶,记住完整存储桶的平均值。我们可以通过结合满桶的平均值和当前的部分桶来安全地计算总体平均值。当我们达到10个满桶时,我们会创建一个更大的桶,容量为100.
要计算总平均值,我们首先计算“10s”的平均值,然后将其与“100s”相结合。这种模式重复“1,000s”“10,000s”等等。在每个阶段,我们只需要考虑两个比前一个大10倍的水平。
答案 1 :(得分:2)
使用double total; unsigned long long count;
。您仍然应该担心准确性,但与float
相比,问题要小得多。
答案 2 :(得分:1)
如何使用任意精度算法?
您可以在维基百科上使用的库列表:http://en.wikipedia.org/wiki/Bignum#Libraries
大多数任意精度算术库在存储的位数填满可用内存之前不会溢出(这是不太可能的)。
答案 3 :(得分:1)
你想使用kahan的求和算法:
http://en.wikipedia.org/wiki/Kahan_summation_algorithm
另请参阅有关求和中的错误的部分 “每个计算机科学家应该知道的关于浮点运算的内容”
答案 4 :(得分:0)
您可以使用这些特殊数据类型,其中整数可以无限增长,直到RAM已满。
答案 5 :(得分:0)
我也在想这个。我认为该解决方案可以根据“移动针头”的新价值来发挥作用。它仅将其移动为对到目前为止的平均值有贡献的先前值的数量的一个因数(自身加1)。随着输入的增加,它会失去准确性,但平均而言应该是可以接受的。 这是一些似乎有效的Java代码。我在这里使用浮点数和整数来证明它可以满足这些限制,但是您可以使用double来获得准确性。这只是为了让您了解如何对近似最大整数数组进行平均。您将需要跟踪输入的总数和当前平均值,而不是输入的总和。如果您的输入总数接近MAX_INT,则最终将不起作用,您应该使用上面的存储桶建议,但是在大多数情况下,这是非常困难的。
public float calcAverageContinuous(int[] integers)
{
float ave = 0;
for (int i = 0; i < integers.length; i++) {
ave += (((float)integers[i] - ave) / (float)(i + 1));
}
return ave;
}