網頁標題: [教學] 資料搜尋的技巧 (二分搜尋法 BinarySearch) [打印本頁]
 
﹗﹗﹗觀看留言:此文章已經有1則留言 ﹗﹗﹗


標題: [教學] 資料搜尋的技巧 (二分搜尋法 BinarySearch) [打印本頁]

--------------------------------------------------------------------------------
作者: yfchang  時間: 2008-7-25 02:13   標題: [教學] 資料搜尋的技巧 (二分搜尋法 BinarySearch)

版權宣告
本文內容可以讓非營利機關或個人進行非有償性教學,
或由學承電腦內講師採用,
唯請註明本文出處及原作者,
其他目的之使用請聯絡作者取得授權。
email : yfchang[at]cs.pu.edu.tw
出處請註明
  網站: 學承電腦技術論壇
  文章: 資料搜尋的技巧 (二分搜尋法 BinarySearch) 教學文件
  作者: 學承電腦講師張演富



前言
在利用程式整理資料時, 常常會遇到搜尋的問題,
在這篇文章將要介紹一個搜尋方法--二分搜尋法 (Binary Search, 註1)
讓程式搜尋資料時能更有效率。

本教學文章為6/22 台中分校C++專業課程的補充文件, 以C++語言為實作例子。

註1:有人翻譯為"二元搜尋法"或是"二進位搜尋法", 筆者認為二分搜尋法較貼切方法本意。



線性搜尋法 (Linear Search)
首先, 以下是一個數列, 其中包含了已排序的八個數字,
0, 1, 3, 4, 6, 7, 9, 10
當程式需要檢驗 "6是否在這數列中"
一般最直接的方法就是從頭比較:
0!=6, 1!=6, 3!=6, 4!=6, 6=6 and found it (共比較五次才找到).
這樣從以一個順序的比較方法就稱為線性搜尋法(Linear Search)。

很顯然的, 當資料量大的時候, 整個搜尋的比較次數也會增大,
例如 當資料有一萬筆, 若所要的資料在最後一筆或找不到的時候,
就必須比較一萬次。我們將上述的數列中每一筆資料所需比較次數作成如下表:


從這表格中就可以推出當資料量為n 筆時, 它的平均比較次數就會是
(1+2+3+.....+n)/n = (n+1)/2
實際上這個技巧所花的比較次數是可以降低的,
就好像當我們查字典時, 不會從A找到Z,
而是會在大約的位置翻開, 查看所需的單字是在前方還是後方,
再逼近所需的頁面, "二分搜尋法"的精神就有點像這樣的方式。



二分搜尋法 (Binary Search)
二分搜尋法的基本精神就是比較數列中的中間資料的值,
根據其大小來判斷所搜尋的資料是在中間值的前半段或是後半段,
例如上述的例子搜尋7是否在 "0, 1, 3, 4, 6, 7, 9, 10" 的數列中,
首先會比較第 (1+8)/2=4 個的資料, 就是4, 而4<6
所以所要搜尋的資料在4的後半段, 因此, 所需搜尋的數列就縮小成 6 (index=5)~10 (index=8) [註2]

註2: 有些書上會把"4"放在後半或前半, 但是在縮小時還是應該要把它拿掉, 以節省比較次數。
另外在程式設計中, 索引值一般都是從0開始, 這個例子的索引值是從1開始, 這樣的差別是不影響計算的。
下一步就是比較第 (5+8)/2 = 6 個的資料, 就是7, 而7>6, 所以所要搜尋的資料在7的前半段,

因此, 所需搜尋的數列就縮小成 6 (index=5)~6 (index=5), 最後就找到所需的資料了。


由於這樣的方法在每個iteration (回合)會砍掉一半的資料, 因此也降低了比較的次數
iteration 1: 8/2 = 4
iteration 2: 4/2 = 2
iteration 3: 2/2 = 1 因為只有一筆資料, then found or not found.

所以這個case, 就是比較了三次。

我們將比較的流程以一個樹狀圖來表示如下, 當搜尋的值比節點內的值小時, 則走向左邊的節點,
反之則走向右邊, 整個樹形最深的位置也是只有經過四個節點的比較。
當數列量為n個時,其最差的比較次數就是log2n+1次, 
若每個資料都以最差的情況計算, 則平均就是比較log2n+1次。
這個推法可能不是那麼的明顯, 其實樹形的深度是跟 log28 = 3 有關係 (2^3 =8 ) [註3]
註3: 有人將比較次數算做需要幾個回合, 意思就是要做幾次切分, 在這圖中最深需要過三個箭頭, 因此也有人算做 log2n。


和Linear Search 來比較, (n+1)/2 的增加速度是比 logn來的快很多,
如下圖紅色是線性的曲線 (y = (n+1)/2), 黑色的是log的曲線 (y = log2n), 整個增加的速度是差很多的,
因此對於大量的資料而言, Binary Search 會比Linear Search 來的有效率。



程式實作

數列的部份, 因為每一筆資料都是數字的格式, 因此我們用(data)陣列來儲存它。
請注意陣列中的索引值是以0為開始, 所以八筆資料的索引值為 0~7。
另外由於在每個回合必須將一段資料忽略, 所以再用start ,end 儲存目前所需搜尋的索引值區間
middle 儲存目前所需考慮的中間資料的索引值

========== 程式碼開始 =============
int data[] = {0,1,3,4,6,7,9,10};  //資料數列
int start =0;               //搜尋區間的起始索引值
int end = 7;               //搜尋區間的結束索引值
int middle = (start + end)/2;    //中間資料的索引值
int search = 6;              //所要搜尋的資料, 假設搜尋6
while (data[middle]!= search){
  if(data[middle]>search){     //當中間值比搜尋值大, 則搜尋資料在左半
    end = middle-1;          //將結束索引值指向中間值, 則下一回合的搜尋區間在前半段
  }
  else {
    start = middle+1;         //否則則在後半段, +/-1的作用在於把middle的資料忽略掉, 加快計算
  }
  if(start>end){              //若起始值比結束還要大, 則表示資料中沒有搜尋的值
    cout<<"Data "<   }
  middle = (start + end)/2;      //更新middle 值
}

if(!start>end){                //若索引值正常, 且while 迴圈正常結束, 則為match
  cout<<"data "< ========== 程式碼結束 =============
table.JPG (11.93 KB)

2008-7-25 02:13
s1.JPG (14.78 KB)

2008-7-25 02:13
s2.JPG (14.64 KB)

2008-7-25 02:13
s3.JPG (14.33 KB)

2008-7-25 02:13
log-linear.JPG (14.36 KB)

2008-7-25 02:54
decisiontree.JPG (8.43 KB)

2008-7-25 11:03





--------------------------------------------------------------------------------
歡迎光臨 Pccenter技術論壇 (http://pforum.pccenter.com.tw/) Powered by Discuz! 5.5.0

資料搜尋字:http://pforum.pccenter.com.tw/redirect.php?tid=4521&goto=lastpost


本文張貼者:校校鴿〔張貼時間:民國99年4月10日(星期六)14點45分〕 | 寫信給校校鴿

部落格首頁


學習的故鄉首頁
本站公告:〔您越需要我們,我們就越有創意〕 本站說明書:〔發現故鄉還有改進的地方,請來信告訴原丁們〕
觀察應用學習點數 :〔咱的故鄉有您的參與,會使我們有更大的發揮空間,展現更豐富精彩的學習畫面〕 〔期待藉由無障礙網頁設計,能讓視障小朋友更愛看書、更愛寫作且更愛學習〕:盲用電腦「心得分享」
〔為了讓我們有乾淨的學習環境,請勿任意在本站散播商業廣告與不合法文件或聯結〕:本站宣示