为什么左+(右 - 左)/ 2不会溢出?

时间:2014-11-27 10:09:25

标签: integer-overflow

在本文中:http://googleresearch.blogspot.sg/2006/06/extra-extra-read-all-about-it-nearly.html,它提到最快的排序算法有一个错误(左+右)/ 2,它指出解决方案使用的是left+(right-left)/2而不是{{1} }。 解决方案也在问题Bug in quicksort example (K&R C book)?

中给出

我的问题是为什么(left+right)/2可以避免溢出?怎么证明呢?提前谢谢。

6 个答案:

答案 0 :(得分:8)

根据定义,您有left < right

因此,right - left > 0,以及left + (right - left) = right(来自基本代数)。

因此left + (right - left) / 2 <= right。因此,不会发生溢出,因为操作的每一步都受right的值限制。


相比之下,请考虑错误的表达式(left + right) / 2left + right >= right,由于我们不知道leftright的值,因此该值完全有可能溢出。

答案 1 :(得分:3)

基本逻辑。

  1. 根据定义left <= MAX_INT
  2. 根据定义right <= MAX_INT
  3. left+(right-left)等于right,每{2}已经<= MAX_INT
  4. 所以left+(right-left)/2 必须也是<= MAX_INT,因为x/2始终小于x
  5. 与原始

    比较
    1. 根据定义left <= MAX_INT
    2. 根据定义right <= MAX_INT
    3. 因此left+right <= MAX_INT
    4. 所以(left+right)/2 <= MAX_INT
    5. 其中语句3显然是错误的,因为left可以是MAX_INT(语句1),right也可以(语句2)。

答案 2 :(得分:3)

假设(为了使示例更容易),最大整数为100,left = 50right = 80。如果你使用天真的公式:

int mid = (left + right)/2;

添加会导致130溢出。

如果您改为:

int mid = left + (right - left)/2;

你不能在(right - left)中溢出,因为你从较大的数字中减去一个较小的数字。这总是导致更小的数字,因此它不可能超过最大值。例如。 80 - 50 = 30

由于结果是leftright的平均值,因此它必须位于它们之间。由于它们都小于最大整数,它们之间的任何东西也都小于最大值,所以没有溢出。

答案 3 :(得分:0)

一个简单的工作示例将显示它。为简单起见,假设数字溢出999以上。如果我们有:

left = 997
right = 999

然后:

left + right = 1995

在我们到达/2之前已经溢出。但是:

right - left = 2
(right-left)/2 = 1
left + (right-left)/2 = 997 + 1 = 998

所以我们避免了溢出。

更普遍(正如其他人所说):如果leftright都在范围内(假设为right > left),则(right-left)/2将在范围内,因此必须left + (right-left)/2,因为这必须小于right(因为您将left增加了right之间差距的一半。

答案 4 :(得分:0)

(这是一个直观的解释而不是证明。)

假设您的数据为unsigned charleft = 100right = 255(因此right为范围边缘)。 如果你执行left + right,那么你将获得355,这不符合unsigned char范围,因此会溢出。

但是,(right-left)/2的数量为Xleft + X < right < MAXMAX,其中unsigned char为{{1}}为255。这样,您可以确保总和永远不会溢出。

答案 5 :(得分:0)

由于Java中的int数据类型为32位(假定是一种编程语言),因此超过32位的任何值都会被翻转。用数字表示,这意味着在Integer.MAX_VALUE(2147483647)上增加1后,返回值将为-2147483648。

来到上面的问题,我们假设以下内容:

int left = 1;
int right = Integer.MAX_VALUE;
int mid;

案例1:

mid = (left +right)/2; 
//Here the value of left + right would be -2147483648 which would overflow.

案例2:

mid = left + (left - right)/2;
//This would not have the same problem as above as the value would never exceed "right".

理论上:

这两个值均与 left +(right-left)/ 2 =(2 * left + right-left)/ 2 =(left + right)/ 2

希望这可以回答您的问题。