mar*_*nus 34 language-agnostic algorithm floating-point precision
想象一下,你有各种各样大小的浮点数.计算总和的最正确方法是什么,误差最小?例如,当数组看起来像这样:
[1.0, 1e-10, 1e-10, ... 1e-10.0]
Run Code Online (Sandbox Code Playgroud)
然后你用一个简单的循环从左到右加起来,比如
sum = 0
numbers.each do |val|
sum += val
end
Run Code Online (Sandbox Code Playgroud)
无论何时加起来,较小的数字可能会低于精度阈值,因此误差会越来越大.据我所知,最好的方法是对数组进行排序并开始将数字从最低到最高相加,但我想知道是否有更好的方法(更快,更精确)?
编辑:谢谢你的答案,我现在有一个工作代码,完美地总结了Java中的双重值.它是获胜答案的Python帖子的直接端口.该解决方案通过了我的所有单元测试.(这里有更长但优化的版本Summarizer.java)
/**
* Adds up numbers in an array with perfect precision, and in O(n).
*
* @see http://code.activestate.com/recipes/393090/
*/
public class Summarizer {
/**
* Perfectly sums up numbers, without rounding errors (if at all possible).
*
* @param values
* The values to sum up.
* @return The sum.
*/
public static double msum(double... values) {
List<Double> partials = new ArrayList<Double>();
for (double x : values) {
int i = 0;
for (double y : partials) {
if (Math.abs(x) < Math.abs(y)) {
double tmp = x;
x = y;
y = tmp;
}
double hi = x + y;
double lo = y - (hi - x);
if (lo != 0.0) {
partials.set(i, lo);
++i;
}
x = hi;
}
if (i < partials.size()) {
partials.set(i, x);
partials.subList(i + 1, partials.size()).clear();
} else {
partials.add(x);
}
}
return sum(partials);
}
/**
* Sums up the rest of the partial numbers which cannot be summed up without
* loss of precision.
*/
public static double sum(Collection<Double> values) {
double s = 0.0;
for (Double d : values) {
s += d;
}
return s;
}
}
Run Code Online (Sandbox Code Playgroud)
dF.*_*dF. 24
对于"更精确":Python Cookbook中的这个配方具有总和算法,可以保持完整的精度(通过跟踪小计).代码在Python中,但即使您不了解Python,它也足以适应任何其他语言.
所有细节都在本文中给出.
有很多算法,取决于你想要什么。通常他们需要跟踪部分总和。如果你只保留 x[k+1] - x[k] 的总和,你会得到 Kahan 算法。如果您跟踪所有部分和(因此产生 O(n^2) 算法),您会得到 @dF 的答案。
请注意,除了您的问题之外,对不同符号的数量求和是非常有问题的。
现在,有比跟踪所有部分总和更简单的方法:
个人经验表明,您通常不需要比卡汉的方法更花哨的东西。