熱線電話:13121318867

登錄
首頁精彩閱讀k-d樹查詢算法的偽代碼_實際用法
k-d樹查詢算法的偽代碼_實際用法
2014-12-03
收藏

k-d樹查詢算法的偽代碼_實際用法

    k-d樹查詢算法的偽代碼如下所示:

  1. 算法:k-d樹最鄰近查找  
  2. 輸入:Kd,    //k-d tree類型  
  3.      target  //查詢數據點  
  4. 輸出:nearest, //最鄰近數據點  
  5.      dist      //最鄰近數據點和查詢點間的距離  
  6.   
  7. 1. If Kd為NULL,則設dist為infinite并返回  
  8. 2. //進行二叉查找,生成搜索路徑  
  9.    Kd_point = &Kd;                   //Kd-point中保存k-d tree根節點地址  
  10.    nearest = Kd_point -> Node-data;  //初始化最近鄰點  
  11.   
  12.    while(Kd_point)  
  13.      push(Kd_point)到search_path中; //search_path是一個堆棧結構,存儲著搜索路徑節點指針  
  14.   
  15.       If Dist(nearest,target) > Dist(Kd_point -> Node-data,target)  
  16.        nearest  = Kd_point -> Node-data;    //更新最近鄰點  
  17.        Min_dist = Dist(Kd_point,target);  //更新最近鄰點與查詢點間的距離  ***/  
  18.      s = Kd_point -> split;                       //確定待分割的方向  
  19.   
  20.      If target[s] <= Kd_point -> Node-data[s]     //進行二叉查找  
  21.        Kd_point = Kd_point -> left;  
  22.      else  
  23.        Kd_point = Kd_point ->right;  
  24.    End while  
  25.   
  26. 3. //回溯查找  
  27.    while(search_path != NULL)  
  28.      back_point = 從search_path取出一個節點指針;   //從search_path堆棧彈棧  
  29.      s = back_point -> split;                      //確定分割方向  
  30.   
  31.      If Dist(target[s],back_point -> Node-data[s]) < Max_dist   //判斷還需進入的子空間  
  32.        If target[s] <= back_point -> Node-data[s]  
  33.          Kd_point = back_point -> right;  //如果target位于左子空間,就應進入右子空間  
  34.        else  
  35.          Kd_point = back_point -> left;    //如果target位于右子空間,就應進入左子空間  
  36.        將Kd_point壓入search_path堆棧;  
  37.   
  38.      If Dist(nearest,target) > Dist(Kd_Point -> Node-data,target)  
  39.        nearest  = Kd_point -> Node-data;                 //更新最近鄰點  
  40.        Min_dist = Dist(Kd_point -> Node-data,target);  //更新最近鄰點與查詢點間的距離的  
  41.    End while   

    讀者來信點評@yhxyhxyhx,在“將Kd_point壓入search_path堆棧;”這行代碼后,應該是調到步驟2再往下走二分搜索的邏輯一直到葉結點,我寫了一個遞歸版本的二維kd tree的搜索函數你對比的看看:

  1. void innerGetClosest(NODE* pNode, PT point, PT& res, int& nMinDis)  
  2. {  
  3.     if (NULL == pNode)  
  4.         return;  
  5.     int nCurDis = abs(point.x - pNode->pt.x) + abs(point.y - pNode->pt.y);  
  6.     if (nMinDis < 0 || nCurDis < nMinDis)  
  7.     {  
  8.         nMinDis = nCurDis;  
  9.         res = pNode->pt;  
  10.     }  
  11.     if (pNode->splitX && point.x <= pNode->pt.x || !pNode->splitX && point.y <= pNode->pt.y)  
  12.         innerGetClosest(pNode->pLft, point, res, nMinDis);  
  13.     else  
  14.         innerGetClosest(pNode->pRgt, point, res, nMinDis);  
  15.     int rang = pNode->splitX ? abs(point.x - pNode->pt.x) : abs(point.y - pNode->pt.y);  
  16.     if (rang > nMinDis)  
  17.         return;  
  18.     NODE* pGoInto = pNode->pLft;  
  19.     if (pNode->splitX && point.x > pNode->pt.x || !pNode->splitX && point.y > pNode->pt.y)  
  20.         pGoInto = pNode->pRgt;  
  21.     innerGetClosest(pGoInto, point, res, nMinDis);  
  22. }  

    下面,以兩個簡單的實例(例子來自圖像局部不變特性特征與描述一書)來描述最鄰近查找的基本思路。

2.5.2、舉例:點(2.1,3.1)

    星號表示要查詢的點(2.1,3.1)。通過二叉搜索,順著搜索路徑很快就能找到最鄰近的近似點,也就是葉子節點(2,3)。而找到的葉子節點并不一定就是最鄰近的,最鄰近肯定距離查詢點更近,應該位于以查詢點為圓心且通過葉子節點的圓域內。為了找到真正的最近鄰,還需要進行相關的‘回溯'操作。也就是說,算法首先沿搜索路徑反向查找是否有距離查詢點更近的數據點。

    以查詢(2.1,3.1)為例:

  1. 二叉樹搜索:先從(7,2)點開始進行二叉查找,然后到達(5,4),最后到達(2,3),此時搜索路徑中的節點為<(7,2),(5,4),(2,3)>,首先以(2,3)作為當前最近鄰點,計算其到查詢點(2.1,3.1)的距離為0.1414,
  2. 回溯查找:在得到(2,3)為查詢點的最近點之后,回溯到其父節點(5,4),并判斷在該父節點的其他子節點空間中是否有距離查詢點更近的數據點。以(2.1,3.1)為圓心,以0.1414為半徑畫圓,如下圖所示。發現該圓并不和超平面y = 4交割,因此不用進入(5,4)節點右子空間中(圖中灰色區域)去搜索;
  3. 最后,再回溯到(7,2),以(2.1,3.1)為圓心,以0.1414為半徑的圓更不會與x = 7超平面交割,因此不用進入(7,2)右子空間進行查找。至此,搜索路徑中的節點已經全部回溯完,結束整個搜索,返回最近鄰點(2,3),最近距離為0.1414。


2.5.3、舉例:查詢點(2,4.5)

    一個復雜點了例子如查找點為(2,4.5),具體步驟依次如下:

  1. 同樣先進行二叉查找,先從(7,2)查找到(5,4)節點,在進行查找時是由y = 4為分割超平面的,由于查找點為y值為4.5,因此進入右子空間查找到(4,7),形成搜索路徑<(7,2),(5,4),(4,7)>,但(4,7)與目標查找點的距離為3.202,而(5,4)與查找點之間的距離為3.041,所以(5,4)為查詢點的最近點;
  2. 以(2,4.5)為圓心,以3.041為半徑作圓,如下圖所示??梢娫搱A和y = 4超平面交割,所以需要進入(5,4)左子空間進行查找,也就是將(2,3)節點加入搜索路徑中得<(7,2),(2,3)>;于是接著搜索至(2,3)葉子節點,(2,3)距離(2,4.5)比(5,4)要近,所以最近鄰點更新為(2,3),最近距離更新為1.5;
  3. 回溯查找至(5,4),直到最后回溯到根結點(7,2)的時候,以(2,4.5)為圓心1.5為半徑作圓,并不和x = 7分割超平面交割,如下圖所示。至此,搜索路徑回溯完,返回最近鄰點(2,3),最近距離1.5。

    上述兩次實例表明,當查詢點的鄰域與分割超平面兩側空間交割時,需要查找另一側子空間,導致檢索過程復雜,效率下降。

    一般來講,最臨近搜索只需要檢測幾個葉子結點即可,如下圖所示:  

    但是,如果當實例點的分布比較糟糕時,幾乎要遍歷所有的結點,如下所示:

    研究表明N個節點的K維k-d樹搜索過程時間復雜度為:tworst=O(kN1-1/k)。

    同時,以上為了介紹方便,討論的是二維或三維情形。但在實際的應用中,如SIFT特征矢量128維,SURF特征矢量64維,維度都比較大,直接利用k-d樹快速檢索(維數不超過20)的性能急劇下降,幾乎接近貪婪線性掃描。假設數據集的維數為D,一般來說要求數據的規模N滿足N?2D,才能達到高效的搜索。所以這就引出了一系列對k-d樹算法的改進:BBF算法,和一系列M樹、VP樹、MVP樹等高維空間索引樹(下文2.6節kd樹近鄰搜索算法的改進:BBF算法,與2.7節球樹、M樹、VP樹、MVP樹)。

數據分析咨詢請掃描二維碼

若不方便掃碼,搜微信號:CDAshujufenxi

數據分析師資訊
更多

OK
客服在線
立即咨詢
日韩人妻系列无码专区视频,先锋高清无码,无码免费视欧非,国精产品一区一区三区无码
客服在線
立即咨詢