实现数学公式时出现溢出问题



我听说,在计算平均值时,start+(end-start)/2与(start+end)/2不同,因为后者会导致溢出。我不太明白为什么第二个会导致溢出,而第一个则不然。实现可以避免溢出的数学公式的通用规则是什么?

假设您正在使用最大整数值为 10 的计算机,并且想要计算 5 和 7 的平均值。

第一种方法(开始+(结束-开始)/2)给出

5 + (7-5)/2 == 5 + 2/2 == 6

第二种方法(begin + end)/2 给出了溢出,因为中间 12 值超过了我们接受的最大值 10 并"包装"到其他东西(如果您使用无符号数字,它通常会换回零,但如果你的数字是有符号的,你可能会得到一个负数!

12/2 => overflow occurs => 2/2 == 1

当然,在实际计算机中,整数以 2^32 而不是 10 这样的大值溢出,但想法是一样的。不幸的是,据我所知,没有摆脱溢出的"通用"方法,这在很大程度上取决于您使用的特定算法。然后,事情变得更加复杂。根据您在引擎盖下使用的数字类型,您可以获得不同的行为,除了溢出和下溢之外,还需要担心其他类型的数字错误。

您的两个公式都会溢出,但在不同的情况下:

  • startend都接近范围同一侧的整数限制(即正或负)时,(start+end)/2公式的(start+end)部分将溢出。
  • start为正数而end为负数时,start+(end-start)/2公式的(end-start)部分将溢出,并且这两个值都接近可表示整数值的各自末端。

没有"通用"规则,您可以逐案进行:查看公式的某些部分,考虑可能导致溢出的情况,并想出避免它的方法。例如,可以显示start+(end-start)/2公式,以避免在对具有相同符号的值求平均值时溢出。

这是困难的方法;简单的方法是使用更高容量的表示来获得中间结果。例如,如果使用long long而不是int进行中间计算,并且仅在完成后将结果复制回int,则假设最终结果适合int,则可以避免溢出。

在处理整数时,在采用此类策略时,您可能关心整数溢出。

请注意,使用公式b+(b-a)/2您需要确保a <= b .否则,您可能会在可能的值范围的下限处遇到相同的问题。想想a/2+b/2.但是,这种方法还有其他缺点。


在处理浮点数时,还存在另一个问题,即灾难性取消。由于浮点表示的有效位数有限,因此在添加大量数字时会丢失精度(即使这只是中间步骤)。

为了解决数值稳定性的问题,例如可以使用此算法(略微改编自维基百科):

def online_mean(data):
  n = 0
  mean = 0
  for x in data:
    n = n + 1
    delta = x - mean
    mean = mean + delta/n
  return mean

我不知何故觉得与您上面提出的公式有关系......

在二叉搜索中,我们将编写以下代码:

if(start > end){
   return;
}
int mid = start + (end - start) / 2;

通过使用start + (end - start) / 2,我们可以避免@dasblinkenlight指出的问题

如果我们使用 (start + end) / 2 ,它将溢出,如 Dasblinkenlight 所示

相关内容

  • 没有找到相关文章

最新更新