《编程珠玑(第2版•修订版)》—第2章2.2节无处不在的二分搜索
本節(jié)書摘來自異步社區(qū)《編程珠璣(第2版?修訂版)》一書中的第2章2.2節(jié)無處不在的二分搜索,作者【美】Jon Bentley,更多章節(jié)內(nèi)容可以訪問云棲社區(qū)“異步社區(qū)”公眾號(hào)查看。
2.2 無處不在的二分搜索
我想到的一個(gè)數(shù)在1到100之間,你來猜猜看。50?太小了。75?太大了。如此,游戲進(jìn)行下去,直到你猜中我想到的數(shù)為止。如果我的整數(shù)位于1到n之間,那么你可以在log2n次之內(nèi)猜中。如果n是1 000,10次就可以完成;如果n是100萬,則最多20次就可以完成。
這個(gè)例子引出了一項(xiàng)可以解決眾多編程問題的技術(shù):二分搜索。初始條件是已知一個(gè)對(duì)象存在于一個(gè)給定的范圍內(nèi),而一次探測(cè)操作可以告訴我們?cè)搶?duì)象是否低于、等于或高于給定的位置。二分搜索通過重復(fù)探測(cè)當(dāng)前范圍的中點(diǎn)來定位對(duì)象。如果一次探測(cè)沒有找到該對(duì)象,那么我們將當(dāng)前范圍減半,然后繼續(xù)下一次探測(cè)。當(dāng)找到所需要的對(duì)象或范圍為空時(shí)停止。
在程序設(shè)計(jì)中二分搜索最常見的應(yīng)用是在有序數(shù)組中搜索元素。在查找項(xiàng)50時(shí),算法進(jìn)行如下探測(cè)。
眾所周知,二分搜索程序要正確運(yùn)行很困難。在第4章中我們將詳細(xì)研究其代碼。
順序搜索在搜索一個(gè)具有n個(gè)元素的表時(shí),平均需要進(jìn)行n/2次比較,而二分搜索僅僅進(jìn)行不超過log2n次的比較就可以完成。這在系統(tǒng)性能上會(huì)造成巨大的差異。下面的故事來自于《ACM通訊》的實(shí)例研究“TWA Reservation System”。
我們有一個(gè)執(zhí)行線性搜索的程序,可以在1秒鐘內(nèi)對(duì)一塊非常巨大的內(nèi)存塊完成100次搜索。隨著網(wǎng)絡(luò)的增長(zhǎng),處理每條消息所需的平均CPU時(shí)間上升了0.3毫秒,這對(duì)我們來說是巨大的變化。我們發(fā)現(xiàn)問題的根源是線性搜索。把程序改為使用二分搜索以后,該問題消失了。
我在許多系統(tǒng)中也遇到過相同的問題。程序員在開始的時(shí)候使用簡(jiǎn)單的順序搜索數(shù)據(jù)結(jié)構(gòu),這在開始的時(shí)候通常都足夠快。當(dāng)搜索變得太慢的時(shí)候,對(duì)表進(jìn)行排序并使用二分搜索通常可以消除瓶頸。
但是二分搜索的故事并沒有在快速搜索有序數(shù)組這里終止。Roy Weil將該技術(shù)應(yīng)用于清理一個(gè)約1000行的輸入文件,其中僅包含一個(gè)錯(cuò)誤行。很不幸,肉眼看不出錯(cuò)誤行。只能通過在程序中運(yùn)行文件的一個(gè)(起始)部分并且觀察到離奇錯(cuò)誤的答案來辨別,這將會(huì)花費(fèi)幾分鐘的時(shí)間。他的前任調(diào)試人員試圖通過每次運(yùn)行整個(gè)程序中的少數(shù)幾行程序來找出錯(cuò)誤行,但只在取得解決方案的道路上前進(jìn)了一點(diǎn)點(diǎn)。Weil是如何僅僅運(yùn)行10次程序就找到罪魁禍?zhǔn)椎哪?#xff1f;
經(jīng)過前面的熱身,我們現(xiàn)在來攻克問題A。輸入為順序文件(考慮磁帶或磁盤——雖然磁盤可以隨機(jī)讀寫,但是從頭至尾讀取文件通常會(huì)快得多)。文件包含最多40億個(gè)隨機(jī)排列的32位整數(shù),而我們需要找出一個(gè)不存在于該文件中的32位整數(shù)。(至少缺少一個(gè)整數(shù),因?yàn)橐还灿?32也就是4 294 967 296個(gè)這樣的整數(shù)。)如果有足夠的內(nèi)存,可以采用第1章中介紹的位圖技術(shù),使用536 870 912個(gè)8位字節(jié)形成位圖來表示已看到的整數(shù)。然而,該問題還問到在僅有幾百個(gè)字節(jié)內(nèi)存和幾個(gè)稀疏順序文件的情況下如何找到缺失的整數(shù)?為了采用二分搜索技術(shù),就必須定義一個(gè)范圍、在該范圍內(nèi)表示元素的方式以及用來確定哪一半范圍存在缺失整數(shù)的探測(cè)方法。如何來實(shí)現(xiàn)呢?
我們采用已知包含至少一個(gè)缺失元素的一系列整數(shù)作為范圍,并使用包含所有這些整數(shù)在內(nèi)的文件表示這個(gè)范圍。靈機(jī)一動(dòng)的結(jié)果是通過統(tǒng)計(jì)中間點(diǎn)之上和之下的元素來探測(cè)范圍:或者上面或者下面的范圍具有至多全部范圍的一半元素。由于整個(gè)范圍中有一個(gè)缺失元素,因此我們所需的那一半范圍中必然也包含缺失的元素。這些就是解決該問題的二分搜索算法所需要的主要想法。在翻閱答案查看Ed Reingold是如何做的以前,請(qǐng)嘗試將這些想法組織起來。
對(duì)于二分搜索技術(shù)在程序設(shè)計(jì)中的應(yīng)用來說,這些應(yīng)用僅僅是皮毛而已。求根程序使用二分搜索技術(shù),通過連續(xù)地對(duì)分區(qū)間來求解單變量方程式(數(shù)值分析家稱之為對(duì)分法)。當(dāng)答案11.9中的選擇算法區(qū)分出一個(gè)隨機(jī)元素以后,就對(duì)該元素一側(cè)的所有元素遞歸地調(diào)用自身(這是一種隨機(jī)二分搜索)。其他使用二分搜索的地方包括樹數(shù)據(jù)結(jié)構(gòu)和程序調(diào)試(當(dāng)程序沒有任何提示就意外中止時(shí),你會(huì)從源代碼中哪一部分開始探測(cè)來定位錯(cuò)誤語句呢?)。在上述的每個(gè)例子中,分析程序并對(duì)二分搜索算法做些許修改,可以帶給程序員功能強(qiáng)大的啊哈!靈機(jī)一動(dòng)。
總結(jié)
以上是生活随笔為你收集整理的《编程珠玑(第2版•修订版)》—第2章2.2节无处不在的二分搜索的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: oracle如何处理死锁,Oracle死
- 下一篇: kafka学习总结