【问题标题】:Logical right shift in binary search preventing arithmetic overflow二进制搜索中的逻辑右移防止算术溢出
【发布时间】:2012-04-06 17:14:14
【问题描述】:

显然,在二分搜索实现中:

mid = (low + high)/2

可能导致溢出。我已经阅读了很多文档(例如this),以下内容可以防止该问题:

mid = (low + high) >>> 1 

但是,我没有看到这样做的原因。任何人都可以对此有所了解吗?

【问题讨论】:

  • C 中没有“逻辑右移”之类的东西(没有>>> 运算符)。
  • 谢谢各位,帮了大忙。我确实在那里看到了 C 等效代码,但我的问题更多是关于为什么它会起作用。但是,您的所有回答都帮助我消除了疑虑。
  • 使用正确的类型。这就是 size_t 的发明目的。

标签: c algorithm search


【解决方案1】:

C 中没有“逻辑右移”之类的东西(没有 >>> 运算符),所以您可能在谈论 Java。

这是有效的,因为low 和 high 被假定在 0 到 2^31-1 的范围内(假设我们在这里讨论的是 int)。 low+high 的最大可能值不大于2^32-2,因此可以用unsigned int 表示(如果Java 中存在这样的东西)。 Java中不存在这样的东西,所以我们现在已经溢出了。但是,逻辑移位运算符>>> 将其操作数视为无符号,因此这给出了预期的结果。

【讨论】:

    【解决方案2】:

    >>> 是 Java 中的无符号右移运算符 (ref)。由于mid、low 和high 是有符号整数,所以low 和high 相加可能会溢出为负值。 >>> 忽略此结果的潜在负数并将其向右移动,就好像它是一个无符号数(在 Java 中,没有无符号数)。

    在 C 和 C++ 中,这相当于

    mid = ((unsigned int)low + (unsigned int)high)) >> 1;
    

    (在您链接到的文章中明确提及)。

    这最终与

    相同
    mid = ((unsigned int)low + (unsigned int)high)) / 2;
    

    请注意,您可能不想这样做。如果要使用无符号值,则应坚持使用无符号值,并避免在有符号和无符号之间来回跳动。

    【讨论】:

      【解决方案3】:

      同一个链接说明了使用Java的原因>>>,原因是(低+高)可能超过'mid'可以容纳的最大值:

      在 Programming Pearls Bentley 中说类似的行“将 m 设置为 l 和 u 的平均值,截断到最接近的整数。”在 从表面上看,这个断言可能看起来是正确的,但它失败了 int 变量 low 和 high 的值很大。具体来说,它失败了 如果低和高的总和大于最大正整数 值 (231 - 1)。和溢出为负值,并且该值 除以二时保持负数。在 C 中,这会导致数组索引 越界,结果不可预知。

      它还说明了 C 中的等效操作:

      ......

      在 C 和 C++ 中(没有 >>> 运算符),您可以这样做:

      6: 中 = ((unsigned int)low + (unsigned int)high)) >> 1;

      所以解决办法是完全阅读并理解那篇文章。

      【讨论】:

        【解决方案4】:

        正如其他答案中提到的那样,>>> 不是C 运算符。

        不过,如果你想避免C 溢出,你可以试试这个:

        mid = (high - low)/2 + low;
        

        【讨论】:

          猜你喜欢
          • 2017-11-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-09-25
          • 1970-01-01
          • 2011-04-11
          • 2012-02-19
          • 1970-01-01
          相关资源
          最近更新 更多