2010-11-17 128 views
3

我正在嘗試構建KD樹(靜態大小寫)。我們假設點在x和y座標上排序。KD樹,慢樹構建

對於遞歸的均勻深度,該集合被劃分爲兩個子集,其中垂直線穿過中值x座標。

對於遞歸的奇數深度,該集合被分成兩個子集,水平線穿過中位數y座標。

可以根據x/y座標從排序後的集合中確定中位數。在每次分割集合之前,我正在執行這一步驟。我認爲這會導致樹的建設速度緩慢。

  1. 請你能幫我檢查一下並優化代碼嗎?
  2. 我找不到第k個最近的鄰居,有人能幫我解碼嗎?

非常感謝您的幫助和耐心......

請參見示例代碼:

class KDNode 
{ 
private: 
Point2D *data; 
KDNode *left; 
KDNode *right; 
    .... 
}; 

void KDTree::createKDTree(Points2DList *pl) 
{ 
//Create list 
KDList kd_list; 

//Create KD list (all input points) 
for (unsigned int i = 0; i < pl->size(); i++) 
{ 
kd_list.push_back((*pl)[i]); 
} 

//Sort points by x 
std::sort(kd_list.begin(), kd_list.end(), sortPoints2DByY()); 

//Build KD Tree 
root = buildKDTree(&kd_list, 1); 
} 


KDNode * KDTree::buildKDTree(KDList *kd_list, const unsigned int depth) 
{ 
//Build KD tree 
const unsigned int n = kd_list->size(); 

//No leaf will be built 
if (n == 0) 
{ 
    return NULL; 
} 

//Only one point: create leaf of KD Tree 
else if (n == 1) 
{ 
    //Create one leaft 
    return new KDNode(new Point2D ((*kd_list)[0])); 
} 

//At least 2 points: create one leaf, split tree into left and right subtree 
else 
{ 
    //New KD node 
    KDNode *node = NULL; 

    //Get median index 
    const unsigned int median_index = n/2; 

    //Create new KD Lists 
    KDList kd_list1, kd_list2; 

    //The depth is even, process by x coordinate 
    if (depth%2 == 0) 
    { 
    //Create new median node 
    node = new KDNode(new Point2D((*kd_list)[median_index])); 

    //Split list 
    for (unsigned int i = 0; i < n; i++) 
    { 
    //Geta actual point 
    Point2D *p = &(*kd_list)[i]; 

    //Add point to the first list: x < median.x 
    if (p->getX() < (*kd_list)[median_index].getX()) 
    { 
    kd_list1.push_back(*p); 
    } 

    //Add point to the second list: x > median.x 
    else if (p->getX() > (*kd_list)[median_index].getX()) 
    { 
    kd_list2.push_back(*p); 
    } 
    } 

    //Sort points by y for the next recursion step: slow construction of the tree??? 
    std::sort(kd_list1.begin(), kd_list1.end(), sortPoints2DByY()); 
    std::sort(kd_list2.begin(), kd_list2.end(), sortPoints2DByY()); 

    } 

    //The depth is odd, process by y coordinates 
    else 
    { 

    //Create new median node 
    node = new KDNode(new Point2D((*kd_list)[median_index])); 

    //Split list 
    for (unsigned int i = 0; i < n; i++) 
    { 
    //Geta actual point 
    Point2D *p = &(*kd_list)[i]; 

    //Add point to the first list: y < median.y 
    if (p->getY() < (*kd_list)[median_index].getY()) 
    { 
    kd_list1.push_back(*p); 
    } 

    //Add point to the second list: y < median.y 
    else if (p->getY() >(*kd_list)[median_index].getY()) 
    { 
    kd_list2.push_back(*p); 
    } 
    } 

    //Sort points by x for the next recursion step: slow construction of the tree??? 
    std::sort(kd_list1.begin(), kd_list1.end(), sortPoints2DByX()); 
    std::sort(kd_list2.begin(), kd_list2.end(), sortPoints2DByX()); 

    } 

    //Build left subtree 
    node->setLeft(buildKDTree(&kd_list1, depth +1)); 

    //Build right subtree 
    node->setRight(buildKDTree(&kd_list2, depth + 1)); 

    //Return new node 
    return node; 
} 
} 
+0

如何定義類型'KDList'? – 2010-11-17 10:28:11

+0

@Space:typedef std :: vector KDList; – Ian 2010-11-17 10:31:06

+0

如何定義「Points2DList」? – 2010-11-17 10:33:20

回答

3

不是一個真正的回答你的問題,但我會極力推薦論壇在http://ompf.org/forum/ 他們在那裏爲各種情況下的快速kd樹構造進行了一些很好的討論。也許你會在那裏找到一些靈感。

編輯:
的OMPF論壇以來下降了,雖然直接替換目前可http://ompf2.com/

+0

沒有答案那裏... – Ian 2010-11-18 20:15:07

+1

就像我說的,你可能不會直接在那裏找到答案。但是,如果您積極參與論壇並在那裏提出問題,您很可能會得到答覆,幫助您順利完成任務。如果有一個論壇討論KD樹或其他層次結構,它們的屬性,快速構建方法等等,那就是其中之一。 – Bart 2010-11-18 20:59:51

+0

答案中的鏈接消失了:( – mkb 2012-05-15 21:24:18

5

排序,找到中位數可能是罪魁禍首這裏,因爲這是O(nlogn )而問題在O(n)時間內是可解的。您應該使用nth_element:http://www.cplusplus.com/reference/algorithm/nth_element/。這將在平均線性時間內找到中位數,之後您可以在線性時間內分解向量。在矢量

內存管理也未嘗採取了很多的時間,尤其是對於大型的載體,因爲每一次的矢量的大小加倍所有的元素都被移動。您可以使用向量的保留方法爲新創建的節點中的向量保留足夠的空間,因此它們不需要動態增加,因爲使用push_back添加了新的東西。

如果您絕對需要最佳性能,則應使用較低級別的代碼,而不是使用向量並保留普通數組。第n個元素或「選擇」的算法是現成的,不是太難寫自己:http://en.wikipedia.org/wiki/Selection_algorithm

2

優化KD樹的一些提示:

  • 使用線性時間中位數尋找算法,如QuickSelect。
  • 避免實際使用「節點」對象。您可以使用點只有存儲整棵樹,ZERO附加信息。基本上只需對一組對象進行排序。根節點將處於中間。在查詢時,首先放入根,然後使用堆佈局的重新排列可能更適合CPU內存緩存,但構建起來會更棘手。
1

你的第一個罪魁禍首是排序找到中位數。這幾乎總是K-d樹構造的瓶頸,並且在這裏使用更高效的算法將真正獲得回報。

但是,每次將元素拆分並傳遞給它們時,您也正在構造一對可變大小的向量。

在這裏,我推薦好ol單鏈表。鏈表的美妙之處在於你可以通過改變next指針來指向父指針而不是父指針,從而可以將元素從父母轉移到孩子。

這意味着在構建過程中不需要任何堆棧開銷將元素從父節點傳遞到子節點,只需要聚合要插入到根的初始元素列表。這也應該有奇效,但是如果你想要更快,你可以使用一個固定的分配器爲鏈表(以及樹)有效地分配節點,並有更好的連續性/緩存命中。

最後但並非最不重要的一點,如果您涉及需要K-d樹的密集型計算任務,則需要一個分析器。測量你的代碼,你會看到究竟是什麼在罪魁禍首,並與確切的時間分佈。