为什么M = L +((R-L)/ 2)而不是M =(L + R)/ 2避免C ++溢出?

时间:2014-08-04 07:17:09

标签: c++

您好我正在查看问题的C ++解决方案"假设已排序的数组在事先未知的某个枢轴上旋转。 (即,0 1 2 4 5 6 7可能变为4 5 6 7 0 1 2)。如何有效地在旋转的数组中找到元素?您可以假设数组中不存在重复。"

int rotated_binary_search(int A[], int N, int key) {
    int L = 0;
    int R = N - 1;

    while (L <= R) {
    // Avoid overflow, same as M=(L+R)/2
        int M = L + ((R - L) / 2);
        if (A[M] == key) return M;

    // the bottom half is sorted
        if (A[L] <= A[M]) {
            if (A[L] <= key && key < A[M])
                R = M - 1;
            else
                L = M + 1;
        }
    // the upper half is sorted
        else {
            if (A[M] < key && key <= A[R])
                L = M + 1;
            else 
                R = M - 1;
        }
    }
    return -1;
}

并看到评论说使用M = L +((R - L)/ 2)而不是M =(L + R)/ 2避免溢出。这是为什么?提前

4 个答案:

答案 0 :(得分:12)

因为它确实......

让我们假设您使用无符号字符一分钟(当然也适用于更大的整数)。

如果L为100且R为200,则第一个版本为:

M = (100 + 200) / 2 = 300 / 2 = 22

100 + 200溢出(因为最大的无符号字符是255),你得到100 + 200 = 44(无符号否。)。

第二,另一方面:

M = 100 + (200-100) / 2 = 100 + 100 / 2 = 150

没有溢出。

正如@ user2357112在评论中指出的那样,没有免费的午餐。如果L为负数,则第一个版本可能不起作用,而第一个版本可能不起作用。

答案 1 :(得分:1)

不确定,但如果int的最大限制为100。

R=80 & L = 40
then, 
M=(L+R)/2
M=(120)/2, here 120 is out limits if our integer type, so this causes overflow

然而,

M = L + ((R - L) / 2)  
M = 80 +((40)/2)
M = 80 +20
M =100.

所以在这种情况下,我们永远不会遇到超出整数类型限制的值。因此,这种方法永远不会遇到溢流,理论上。

我希望这个类比会有所帮助

答案 2 :(得分:0)

由于多种原因,评论是错误的。

  • 对于特定问题,溢出的风险可能为零。
  • 重新排序计算并不能保证编译器会按此顺序执行它们。
  • 如果有一系列值可能导致排序溢出,那么还有另一个值范围,重新排序的计算将导致溢出。
  • 如果溢出可能是一个问题,那么应该明确地控制它,而不是隐式控制。

这是断言的绝佳场所。在这种情况下,算法仅在N小于int的最大正范围的一半时有效,所以在断言中说明。

如果算法需要在signed int的整个正范围内工作,则应在断言中明确测试范围,并且应通过引入序列点(例如,分成两个语句)来排序计算

这样做很难。数值计算充满了这些东西。如果可能的话,最好避免。不做自己的研究就不接受随机的建议(即使这样!)。

答案 3 :(得分:0)

它避免了此特定实现中的溢出,该实现在LR非负且L <= R的保证下运行。在这些保证下,显而易见R - L不会溢出,L + ((R - L) / 2)也不会溢出。

一般情况下(即对于LR的任意值)R - L容易溢出L + R,这意味着此技巧无法实现任何效果。