【问题标题】:why left+(right-left)/2 will not overflow?为什么 left+(right-left)/2 不会溢出?
【发布时间】:2014-11-27 10:09:25
【问题描述】:

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

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

【问题讨论】:

    标签: integer-overflow


    【解决方案1】:

    根据定义,您有 left < right

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

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


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

    【讨论】:

      【解决方案2】:

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

      int mid = (left + right)/2;
      

      加法会导致130 溢出。

      如果你这样做:

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

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

      而且由于结果是leftright 的平均值,所以它一定在它们之间。因为它们都小于最大整数,所以它们之间的任何东西也都小于最大值,所以没有溢出。

      【讨论】:

        【解决方案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

        对比原版

        1. 根据定义left <= MAX_INT
        2. 根据定义right <= MAX_INT
        3. 因此left+right <= MAX_INT
        4. 等等(left+right)/2 <= MAX_INT

        语句 3 显然是错误的,因为 left 可以是 MAX_INT(语句 1),right(语句 2)也可以。

        【讨论】:

          【解决方案4】:

          一个简单的例子将展示它。为简单起见,假设数字在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(因为您将leftright 之间的差距增加了一半。

          【讨论】:

            【解决方案5】:

            由于 int 数据类型在 Java 中是 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 + (right - left)/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

            希望这能回答你的问题。

            【讨论】:

              【解决方案6】:

              (这比证明更直观。)

              假设您的数据是unsigned charleft = 100right = 255(所以right 位于范围的边缘)。 如果你做left + right,你会得到355,它不符合unsigned char的范围,所以会溢出。

              但是,(right-left)/2 是一个数量 X,使得 left + X < right < MAX,其中 MAX 是 255 对应于 unsigned char。这样,您可以确保总和永远不会溢出。

              【讨论】:

                猜你喜欢
                • 2019-06-09
                • 1970-01-01
                • 2017-11-24
                • 2019-01-17
                • 2019-08-18
                • 1970-01-01
                • 1970-01-01
                • 2022-01-04
                • 1970-01-01
                相关资源
                最近更新 更多