我听说,在计算平均值时,start +(end-start)/ 2与(start + end)/ 2不同,因为后者会导致溢出。我不太明白为什么第二个会导致溢出而第一个不会导致溢出。实现可以避免溢出的数学公式的通用规则是什么。
答案 0 :(得分:10)
假设您使用的计算机的最大整数值为10,并且您想要计算平均值5和7。
第一种方法(开始+(结束开始)/ 2)给出
5 + (7-5)/2 == 5 + 2/2 == 6
第二种方法(开始+结束)/ 2给出溢出,因为中间12值超过我们接受的最大值10并且“包裹”到其他东西(如果你使用无符号数字,它通常是回到零,但如果您的号码已签名,您可以获得负数!)。
12/2 => overflow occurs => 2/2 == 1
当然,在实际的计算机中,整数溢出的值很大,比如2 ^ 32而不是10,但这个想法是一样的。不幸的是,没有“通用”方法可以摆脱我所知道的溢出,这在很大程度上取决于您使用的是哪种特定算法。然后事件变得更加复杂。根据您使用的数字类型,您可以获得不同的行为,除了上下溢之外还有其他类型的数字错误需要担心。
答案 1 :(得分:3)
你的公式都会溢出,但在不同情况下:
(start+end)
和(start+end)/2
都接近范围同一侧的整数限制时,start
公式的end
部分会溢出(即均为正值)或两者都是否定的。)(end-start)
为正且start+(end-start)/2
为负数时,start
公式的end
部分会溢出,并且这两个值都接近可表示整数的各自末尾值。没有“通用”规则,你可以根据具体情况进行操作:查看公式的部分内容,考虑可能导致溢出的情况,并想出办法避免它。例如,当您使用相同符号对值进行平均时,可以显示start+(end-start)/2
公式以避免溢出。
这是艰难的方式;简单的方法是对中间结果使用更高容量的表示。例如,如果您使用long long
代替int
进行中间计算并仅在完成后将结果复制回int
,则可以避免溢出,假设最终结果适合int
。
答案 2 :(得分:1)
在处理整数时,您可能会在采用此类策略时关注integer overflow。
请注意,使用公式b+(b-a)/2
,您需要确保a <= b
。否则,您可能会在可能的值范围的下限处得到相同的问题。想想a/2+b/2
。然而,这种方法也存在其他缺点。
处理浮点数时会出现另一个问题,catastrophic cancellation。由于浮点表示的有效位数有限,添加大数字时精度会丢失(即使这只是一个中间步骤)。
要解决这个数值稳定性问题,例如可以使用此算法(稍微改编自wikipedia):
def online_mean(data):
n = 0
mean = 0
for x in data:
n = n + 1
delta = x - mean
mean = mean + delta/n
return mean
我不知何故觉得与你上面提到的公式存在关系......
答案 3 :(得分:0)
在二进制搜索中,我们将编写以下代码:
if(start > end){
return;
}
int mid = start + (end - start) / 2;
使用start + (end - start) / 2
,我们可以避免@dasblinkenlight指出的问题
如果我们使用(start + end) / 2
,它会溢出,如dasblinkenlight