哪种算法更准确地计算数字排序数组的总和?


22

给定正数的递增有限序列。以下两种算法中的哪一种更适合计算数字总和?z1,z2,.....zn

s=0; 
for \ i=1:n 
    s=s + z_{i} ; 
end

要么:

s=0; 
for \ i=1:n 
s=s + z_{n-i+1} ; 
end

在我看来,最好将数字从最大到最小相加,因为误差会越来越小。我们也知道,当我们将一个非常大的数字加到一个非常小的数字上时,近似结果可能就是这个大数字。

它是否正确?还有什么可以说的?

Answers:


18

添加任意浮点数通常会产生一些舍入误差,并且舍入误差将与结果的大小成比例。如果您计算一个总和,并首先从最大的数字开始相加,则平均结果将更大。因此,您将以最小的数字开始加法。

但是,如果产生四个和,您将获得更好的结果(并且运行速度更快),例如:以sum1,sum2,sum3,sum4开头并将四个数组元素依次加到sum1,sum2,sum3,sum4。由于每个结果平均仅是原始总和的1/4,因此您的错误小四倍。

更妙的是:成对添加数字。然后成对添加结果。再次成对添加这些结果,依此类推,直到剩下两个数字相加。

非常简单:使用更高的精度。使用long double来计算double的总和。使用double计算浮点数总和。

接近完美:查找前面描述的Kahan算法。最好以最小的数字开头添加。


26

这些是整数还是浮点数?假设它是浮点数,我会选择第一个选项。最好将较小的数字彼此相加,然后再添加较大的数字。使用第二个选项,随着我的增加,您最终将一个小数字增加到一个大数字,这可能会导致问题。这是有关浮点算法的一个很好的资源:每个计算机科学家都应该了解的浮点算法


24

animal_magic的回答是正确的,您应该将数字从最小到最大相加,但是我想举一个例子来说明原因。

假设我们使用的浮点格式可以使我们达到惊人的3位数精度。现在我们要添加十个数字:

[1000, 1, 1, 1, 1, 1, 1, 1, 1, 1]

当然,确切的答案是1009,但我们无法以3位数字的格式得到它。四舍五入到三位数字,我们得到的最准确答案是1010。如果我们将最小和最大相加,则在每个循环上我们将得到:

Loop Index        s
1                 1
2                 2
3                 3
4                 4
5                 5
6                 6
7                 7
8                 8
9                 9
10                1009 -> 1010

这样我们就可以为我们的格式获得最准确的答案。现在假设我们从最大到最小相加。

Loop Index        s
1                 1000
2                 1001 -> 1000
3                 1001 -> 1000
4                 1001 -> 1000
5                 1001 -> 1000
6                 1001 -> 1000
7                 1001 -> 1000
8                 1001 -> 1000
9                 1001 -> 1000
10                1001 -> 1000

由于在每次操作后都会对浮点数进行四舍五入,因此所有加法都将四舍五入,从而使我们的误差从精确值从1增加到9。现在,假设您要添加的数字集是1000,然后是100 1,还是一百万。请注意,要真正准确,您需要将最小的两个数字相加,然后将结果求和到数字集中。


15

对于一般情况,我将使用补偿求和(或Kahan求和)。除非数字已经排序,否则对它们进行排序将比加它们昂贵得多。补偿求和也比排序求和或天真的求和更为准确(请参阅上一个链接)。

至于参考资料,每个程序员都应该了解的浮点算术知识涵盖了足够多的基本知识,因此有人可以在20(+/- 10)分钟内阅读并理解基本知识。戈德伯格的“每位计算机科学家都应该知道的有关浮点算术的知识”是经典的参考书,但是我认识的大多数人都建议读者不要自己详细阅读该论文,因为该论文大约有50页(在某些情况下,甚至更多)印刷品),并以密集的散文形式撰写,因此我很难将其推荐给人们作为一线参考。再次查看该主题是有益的。百科全书是Higham的数值算法准确性和稳定性,涵盖了该材料,以及许多其他算法中的数值误差累积;它也有680页,所以我也不会先看这个参考资料。


2
为了完整起见,在Higham的书中,您会在第82页上找到原始问题的答案:顺序递增是最好的。第(4.6)节讨论方法的选择。
Federico Poloni 2014年

7

前面的答案已经讨论了这个问题,并给出了合理的建议,但是我想提一个额外的怪癖。在大多数现代体系结构上,for无论如何,您描述的循环都将以80位扩展精度执行,这可以确保更高的准确性,因为所有临时变量都将放入寄存器中。因此,您已经有了某种形式的保护措施,以防止数字错误。但是,在更复杂的循环中,中间值将在两次操作之间存储在内存中,因此会被截断为64位。我猜可能是

s=0; 
for \ i=1:n 
    printf("Hello World");
    s=s + z_{i} ; 
end

足以使求和的精度降低(!!)。因此,如果要在检查准确性的同时对代码进行printf调试,请务必小心。

对于感兴趣的人,本文描述了一个广泛使用的数值例程(Lapack的秩显示QR因式分解)中的问题,正是由于这个问题,其调试和分析非常棘手。


1
大多数现代机器都是64位的,即使标量操作也使用SSE或AVX单元。这些单元不支持80位算术运算,并且使用与运算参数相同的内部精度。现在一般不建议使用x87 FPU,大多数64位编译器都需要特殊的选项才能强制使用它。
Hristo Iliev

1
@HristoIliev感谢您的评论,我不知道这一点!
Federico Poloni 2014年

4

在这两个选项中,从较小到较大相加将产生较小的数值误差,然后从较大到较小相加。

但是,> 20年前,在我的“数值方法”课程中,讲师指出了这一点,但我发现,由于累加器和要添加的值之间的相对差异,这仍然带来了不必要的错误。

从逻辑上讲,一个更好的解决方案是在列表中添加2个最小的数字,然后将求和的值重新插入排序后的列表中。

为了证明这一点,我制定了一种算法,该算法可以通过使用从主数组中删除元素时释放的空间来构建总值的辅助数组,从而有效地(在空间和时间上)执行此操作,这些求和值自添加以来就固有地排序是总在增加的价值之和。在每次迭代中,然后检查两个数组的“技巧”以找到2个最小值。


2

由于您没有限制要使用的数据类型,因此要获得完全准确的结果,只需使用任意长度的数字即可,在这种情况下顺序无关紧要。这会慢很多,但是获得完美的确需要时间。


0

使用二叉树加法,即选择分布的平均值(最接近的数字)作为二叉树的根,并通过在图的左侧添加较小的值,在右侧添加较大的值来创建排序的二叉树,依此类推。 。以自下而上的方式递归添加单个父级的所有子节点。这是有效的,因为平均误差会随着求和次数的增加而增加,并且在二叉树方法中,求和次数的数量级以2为底的log n顺序。因此,平均误差会更小。


这与在原始数组中添加相邻对(由于已排序)相同。没有理由将所有值放入树中。
Godric Seer 2014年

0

赫里斯托·伊利耶夫(Hristo Iliev)上面所说的关于64位编译器相对于FPU(AKA NDP)更喜欢SSE和AVX指令的说法是绝对正确的,至少对于Microsoft Visual Studio 2013而言。但是,对于我使用的双精度浮点运算,我发现实际上,使用FPU更快,理论上也更准确。如果对您很重要,我建议您在选择最终方法之前先测试各种解决方案。

在Java中工作时,我经常使用任意精度的BigDecimal数据类型。这太简单了,通常不会注意到速度下降。使用牛顿方法计算具有无限级数和sqrt的先验函数可能需要一毫秒或更长时间,但这是可行的并且非常准确。


0

我只在这里/programming//a/58006104/860099(当您转到此处时,单击以显示代码段并按按钮运行它)

这是一个JavaScript示例,清楚地表明,从最大开始的和给出了更大的误差

arr=[9,.6,.1,.1,.1,.1];

sum     =             arr.reduce((a,c)=>a+c,0);  // =  9.999999999999998
sortSum = [...arr].sort().reduce((a,c)=>a+c,0);  // = 10

console.log('sum:     ',sum);
console.log('sortSum:',sortSum);

本网站不鼓励仅链接的答案。您能解释一下链接中提供的内容吗?
nicoguaro

@nicoguaro我更新了答案-所有答案都很好,但这是具体示例
KamilKiełczewski
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.