二进制搜索中mid =(beg + end)/ 2和mid = beg +(end-beg)/ 2之间有什么区别?

时间:2014-01-08 14:54:40

标签: c++ algorithm binary-search integer-overflow

这是一个问题来自C ++引言第五版问题3.26,我不知道它们之间的区别? 可能是第二个可以避免溢出。

4 个答案:

答案 0 :(得分:15)

  

可能是第二个可以避免溢出。

完全。无法保证beg+end可以表示;但在第二种情况下,中间值以及预期结果不大于end,因此没有溢出的危险。

第二种形式也可用于仿射类型,如指针和其他随机访问迭代器,可以减去它们以给出距离,但不能一起添加。

答案 1 :(得分:1)

一般情况下,两个表达式都无效。例如,第一个表达式无效,因为对于指针或迭代器没有+操作。 如果使用非随机访问迭代器,则第二个表达式无效。例如,使用双向迭代器时。

因此,C ++中的正确​​构造将采用以下方式

mid = std::next( beg, std::distance( beg, end ) / 2 );

答案 2 :(得分:1)

如果我们在更通用的设置中考虑这两行,与二分搜索无关,可以进行以下观察:

你是对的,第二种形式试图避免的问题是溢出,试图表示一个大于最大可表示数字的数字。

对于个体数量的大小有多大没有限制,因此它们可能都大于最大可表示数量的一半。添加它们意味着中间结果(beg + end)可能溢出。

第二种解决方案似乎消除了溢出的风险,但又引入了另一种解决方案。如果值是有符号值,则它们的差异可能会再次溢出(或下溢,具体取决于它们的符号)。无符号值没有问题。

您还没有发布另一种解决方案:

mid = beg/2 + end/2

这解决了溢出和下溢的每个问题,但引入了一个精确丢失的新问题。如果使用整数值,除以2会使结果偏离0.5,将这些结果相加,意味着mid可以关闭1:

mid = 3/2 + 5/2; // mid is 3, instead of the 4 expected

使用浮点值还存在其他精度问题。

回到手头的问题,二进制搜索,很容易看到beg和end是无符号值,所以第二种解决方案总会给出正确的结果。

答案 3 :(得分:1)

答案在书中:

"因为从end返回的迭代器不表示元素,所以它可能 不得增加或解除引用。"

图形上它作为不对称范围是有意义的, [开始,非正式) 或半开放范围。

来自Accelerated C ++,第28页,Koenig。