【问题标题】:what's the difference between mid=(beg+end)/2 and mid=beg+(end-beg)/2 in binary search?二进制搜索中 mid=(beg+end)/2 和 mid=beg+(end-beg)/2 有什么区别?
【发布时间】:2014-01-26 18:00:16
【问题描述】:

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

【问题讨论】:

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


【解决方案1】:

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

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

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

【讨论】:

  • 有趣的是,指向范围的指针(和随机访问迭代器)的空间是仿射空间:因此随机访问迭代器的仿射组合(例如取它们的平均值)在逻辑上是有效的。我们只是缺少 C++ 中的仿射组合原语。
  • @Yakk:谢谢,我绞尽脑汁想记住“仿射”这个词。我学习纯数学已经太久了。
  • 这是一件值得记住的好事情。
【解决方案2】:

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

所以 C++ 中的正确构造如下所示

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

【讨论】:

  • 您何时希望在非随机访问迭代器上运行二分查找?
  • 不只是我。但是 C++ 标准用前向迭代器声明 std::binary_search template bool binary_search(ForwardIterator first, ForwardIterator last, const T& value);
  • 我想如果比较功能非常昂贵是有道理的。但通常它没有意义,因为你失去了运行时间的 O(log n) 限制。
【解决方案3】:

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

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

对于单个数字 beg 和 end 的大小没有限制,因此它们可能都大于最大可表示数字的一半。添加它们意味着中间结果(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 是无符号值,所以第二个解决方案总是会给出正确的结果。

【讨论】:

    【解决方案4】:

    答案在书中:

    "因为从end返回的迭代器不代表一个元素,它可能 不能增加或取消引用。”

    从图形上看,它是一个不对称的范围, [开始,结束) 或半开范围。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-06-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-28
      • 1970-01-01
      相关资源
      最近更新 更多