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

Why M = L + ((R - L) / 2) instead of M=(L+R)/2 avoid overflow in C++?

本文关键字:避免 L+R 溢出 c++ 为什么      更新时间:2023-10-16

你好,我正在寻找问题的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可以避免溢出。为什么呢?提前谢谢

因为…

让我们暂时假设您正在使用无符号字符(当然也适用于更大的整数)。

如果L是100,R是200,第一个版本是:

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

100+200溢出(因为最大的unsigned char是255),并且您得到100+200=44 (unsigned no。除了)。

另一方面,第二个:

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

没有溢出。

正如@user2357112在评论中指出的那样,天下没有免费的午餐。如果L为负,则第二个版本可能无法工作,而第一个版本可以。

不确定,但如果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.

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

我希望这个比喻能有所帮助

这个注释是错误的,原因有很多。

  • 对于特定的问题,溢出的风险可能为零。
  • 重新排序计算并不保证编译器将按照该顺序执行。
  • 如果存在排序可能导致溢出的值范围,则存在另一个重新排序计算将导致溢出的值范围。
  • 如果溢出可能是一个问题,那么应该显式控制,而不是隐式控制。

这是一个非常适合断言的地方。在这种情况下,只有当N小于int的最大正范围的一半时,算法才有效,因此在断言中表示。

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

做好这件事很难。数值计算充满了这些东西。如果可能的话,最好避免。在没有做自己的研究之前,不要接受随机的建议(即使是这个!)。

在这个特定的实现中避免了溢出,保证LR是非负的,L <= R。在这些保证下,很明显R - L不会溢出,L + ((R - L) / 2)也不会溢出。

在一般情况下(即对于LR的任意值),R - LL + R一样容易溢出,这意味着这个技巧没有达到任何目的。