《编程珠玑(第2版•修订版)》—第2章2.2节无处不在的二分搜索

2.2 无处不在的二分搜索
我想到的一个数在1到100之间,你来猜猜看。50?太小了。75?太大了。如此,游戏进行下去,直到你猜中我想到的数为止。如果我的整数位于1到n之间,那么你可以在log2n次之内猜中。如果n是1 000,10次就可以完成;如果n是100万,则最多20次就可以完成。

这个例子引出了一项可以解决众多编程问题的技术:二分搜索。初始条件是已知一个对象存在于一个给定的范围内,而一次探测操作可以告诉我们该对象是否低于、等于或高于给定的位置。二分搜索通过重复探测当前范围的中点来定位对象。如果一次探测没有找到该对象,那么我们将当前范围减半,然后继续下一次探测。当找到所需要的对象或范围为空时停止。

在程序设计中二分搜索最常见的应用是在有序数组中搜索元素。在查找项50时,算法进行如下探测。

众所周知,二分搜索程序要正确运行很困难。在第4章中我们将详细研究其代码。

顺序搜索在搜索一个具有n个元素的表时,平均需要进行n/2次比较,而二分搜索仅仅进行不超过log2n次的比较就可以完成。这在系统性能上会造成巨大的差异。下面的故事来自于《ACM通讯》的实例研究“TWA Reservation System”。

我们有一个执行线性搜索的程序,可以在1秒钟内对一块非常巨大的内存块完成100次搜索。随着网络的增长,处理每条消息所需的平均CPU时间上升了0.3毫秒,这对我们来说是巨大的变化。我们发现问题的根源是线性搜索。把程序改为使用二分搜索以后,该问题消失了。
我在许多系统中也遇到过相同的问题。程序员在开始的时候使用简单的顺序搜索数据结构,这在开始的时候通常都足够快。当搜索变得太慢的时候,对表进行排序并使用二分搜索通常可以消除瓶颈。

但是二分搜索的故事并没有在快速搜索有序数组这里终止。Roy Weil将该技术应用于清理一个约1000行的输入文件,其中仅包含一个错误行。很不幸,肉眼看不出错误行。只能通过在程序中运行文件的一个(起始)部分并且观察到离奇错误的答案来辨别,这将会花费几分钟的时间。他的前任调试人员试图通过每次运行整个程序中的少数几行程序来找出错误行,但只在取得解决方案的道路上前进了一点点。Weil是如何仅仅运行10次程序就找到罪魁祸首的呢?

经过前面的热身,我们现在来攻克问题A。输入为顺序文件(考虑磁带或磁盘——虽然磁盘可以随机读写,但是从头至尾读取文件通常会快得多)。文件包含最多40亿个随机排列的32位整数,而我们需要找出一个不存在于该文件中的32位整数。(至少缺少一个整数,因为一共有232也就是4 294 967 296个这样的整数。)如果有足够的内存,可以采用第1章中介绍的位图技术,使用536 870 912个8位字节形成位图来表示已看到的整数。然而,该问题还问到在仅有几百个字节内存和几个稀疏顺序文件的情况下如何找到缺失的整数?为了采用二分搜索技术,就必须定义一个范围、在该范围内表示元素的方式以及用来确定哪一半范围存在缺失整数的探测方法。如何来实现呢?

我们采用已知包含至少一个缺失元素的一系列整数作为范围,并使用包含所有这些整数在内的文件表示这个范围。灵机一动的结果是通过统计中间点之上和之下的元素来探测范围:或者上面或者下面的范围具有至多全部范围的一半元素。由于整个范围中有一个缺失元素,因此我们所需的那一半范围中必然也包含缺失的元素。这些就是解决该问题的二分搜索算法所需要的主要想法。在翻阅答案查看Ed Reingold是如何做的以前,请尝试将这些想法组织起来。

对于二分搜索技术在程序设计中的应用来说,这些应用仅仅是皮毛而已。求根程序使用二分搜索技术,通过连续地对分区间来求解单变量方程式(数值分析家称之为对分法)。当答案11.9中的选择算法区分出一个随机元素以后,就对该元素一侧的所有元素递归地调用自身(这是一种随机二分搜索)。其他使用二分搜索的地方包括树数据结构和程序调试(当程序没有任何提示就意外中止时,你会从源代码中哪一部分开始探测来定位错误语句呢?)。在上述的每个例子中,分析程序并对二分搜索算法做些许修改,可以带给程序员功能强大的啊哈!灵机一动。

时间: 2024-09-28 15:08:57

《编程珠玑(第2版•修订版)》—第2章2.2节无处不在的二分搜索的相关文章

《Ruby程序员修炼之道》(第2版)—第1章1.3节Ruby扩展和编程库

1.3 Ruby扩展和编程库本节的要点并不是关于Ruby标准库的参考.曾在引言中解释过,本书的目标不是编写一本Ruby语言的参考文档,而是教会读者使用Ruby语言并掌握它,并最终拓宽视野. 相应地,本节的目标是讲述扩展的工作方式,即如何使用Ruby运行这些扩展.它们之间技术实现的不同,并最终能让用户自己编写扩展和库文件的扩展架构. 随Ruby发布的扩展通常全部作为标准库来引用.标准库包括为不同项目和任务所提供的扩展,如数据库管理.网络.数学领域.XML处理等.标准库精密的结构每次改变,哪怕只有一

《Ruby程序员修炼之道》(第2版)—第1章1.1节进入Ruby的世界

第1章 进入Ruby的世界 Ruby程序员修炼之道(第2版) 本章主要内容 Ruby语法的生存工具箱① Ruby基础编程指引:程序编写.保存.运行和错误检查 Ruby安装指南 Ruby的扩展机制 Ruby中易用的命令行工具,包括交互式Ruby解释器(irb) 本书的内容是Ruby基础,而本章是基础中的基石.本章的目标是让读者在开始学习Ruby之前掌握足够的知识和技巧. 接下来读者将看到Ruby的基本语法和技术,以及Ruby的运行机制:如何写一个程序,怎样使用Ruby运行程序,以及如何把一个程序分

《Ruby程序员修炼之道》(第2版)—第1章1.4节易用的Ruby工具和应用程序

1.4 易用的Ruby工具和应用程序 安装Ruby后,就可以得到一组重要的命令行工具,它们被安装在配置信息bindir所指定的文件夹中,通常是/usr/local/bin./usr/bin或者/opt同等的目录中.(可以使用require "rbconfig"去测试一下RbConfig::CONFIG["bindir"]返回的结果.)这些命令行工具具体是以下几个. ruby:解释器. irb:Ruby交互式解释器. - rdoc和ri:Ruby文档工具. rake:

《Ruby程序员修炼之道》(第2版)—第1章1.2节剖析Ruby的安装

1.2 剖析Ruby的安装在系统上安装Ruby意味着在许多磁盘目录中安装了Ruby语言的库和支持文件.大多数时候,Ruby都知道如何找到其所需要的这些目录而不用弹出提示.但是了解Ruby安装的知识对了解Ruby本身大有益处. 查看Ruby的源代码 除了Ruby安装目录体系之外,Ruby的源代码目录也安装好了.如果没有,可以到Ruby的主页中下载.源代码目录中包含了许多在最终安装中出现的Ruby文件和许多已编译为目标文件并安装好的C语言文件.另外,源代码目录包含了一些如ChangeLog和软件授权

《软件测试技术大全:测试基础 流行工具 项目实战(第3版)》—第1章1.6节模拟面试问答

1.6 模拟面试问答 本章介绍的是软件测试相关的背景,以及软件测试的发展情况等.身为软件测试员,应该或多或少地了解软件测试的发展动态,及其相关的历史事件等内容,这样无论是在与同行交流,向开发人员介绍和讲解测试,还是在应聘面试中,都会有更多的话题. 一般在应聘过程中,面试官可能会问到以下一些问题,读者可以根据自己的了解以及在本章中学到的内容做出相应的回答. (1)您觉得目前的软件测试行业的现状是怎样的? 参考答案:目前的软件测试行业在国内正在蓬勃地发展中,但是由于起步比较晚,虽然大部分公司都已经设

编程珠玑--旋转算法

旋转算法出自<编程珠玑>第二章题目.   <编程珠玑>一书对算法是极度推崇,这点意识在我们看书的时候每每都有被灌输.使用一种好的算法往往能使得程序更加漂亮,也很能带给我们程序员某种满足感.   题目:将一个n元一维数组a[n]左移i个位置.例如,当n=8,i=3时,数组abcdefgh旋转为defghabc.请设计一个算法完成这个任务.   1. 块交换法: 分析:将n元一维数组a[n]分解为两块,将第一块存储在临时数组中,将第二块前移i个单位,再将临时数组加入到第二块后面.  

编程珠玑--粗略估算

粗略估算是<编程珠玑>中第七章提到的内容.   这篇文章将"粗略估算"看做是一项工程技术,是程序员必备的一项技能之一. 本人非常同意这个观点.粗略估算是一种把复杂的事情简单化的能力.我们对某个算法的时间复杂度和空间复杂度的估算就是基于这种估算的能力.如果你能较为准确的估算出一个程序的输出结果,如果你能准确估算出这个程序的运行时间,如果你能准确估算出这个项目的开发时间--如果你能拥有这样的能力,该有多么美好啊.所以难怪乎像微软.Google这样的大公司老喜欢出"请计

编程珠玑--位图法排序

题目:一个最多包含n个正整数的文件,每个数都小于n,其中n=10^7,且所有正整数都不重复.求如何将这n个正整数升序排列. 约束:最多有1MB的内存空间可用,有充足的磁盘存储空间.   分析:这个题目的最大亮点是只有1MB的内存空间,我们可以通过计算得出,内存只有1MB可以储存的int(4byte)有10^3*10^3/4=250 000个号码.而包含正整数的文件约为10^7个int大小.这意味着无法将所有文件中的正整数一次读取进入到内存空间中去进行排序算法.因此衍生出下面两种方法:   方法1

《C++入门经典(第5版•修订版)》——第2章 程序的组成部分

第2章 程序的组成部分 C++入门经典(第5版•修订版)本章介绍如下内容: 为何使用C++?C++程序的组织结构.注释如何让程序更容易理解?函数的作用.虽然面世30多年,但是C++编程语言的地位比20世纪70年代末出现的其他东西高得多.当前C++仍在风行,还是一种世界级编程语言. 造就它令人惊讶的生命力的原因在于,通过使用C++,只需编写少量的代码,就可创建快速执行的程序,且可在各种计算环境下运行.当今的C++编程功能让您能够生成功能强大的复杂应用程序,适用于商业.商务和开源开发.