一文了解M-Tree
概括
這篇部落格分析了論文Indexing Metric Spaces with M-tree(後文簡稱M-Tree),我將從以下幾個方面介紹它:
- 背景
- M-Tree的結構
- M-Tree的插入
- M-Tree的分裂
- 如何使用M-Tree進行range搜尋
- M-Tree的程式碼
背景
M-tree被設計為了解決最近鄰搜尋問題(NN)或範圍搜尋問題。以生活中的例子作為說明,最近鄰搜尋問題(NN)指的是,給定一個地理位置,在資料集中找到距離給定位置最近的目標(比如最近的飯店,最近的銀行等等)。
範圍搜尋問題指的是,給定一個地理位置和搜尋範圍,在資料集中找到距離小於範圍的所有目標。
M-Tree核心思路
M-tree使用圓形(二維情況)覆蓋空間區域。每個圓涵蓋了一部分資料所在的區域。對於查詢目標,M-tree確定哪個圓和目標區域相交,若相交,則繼續探測對應的圓。若不相交則濾除對應的圓,這意味著該圓涵蓋的資料都不需要進行判斷,減少了需要探測的資料量。本質上,M-tree從上到下就是一個大圓包含小圓的結構。
M-Tree的結構
在這一節分析如果構建M-tree。M-Tree使用圓來劃分割槽域,因此每個結點存在一個圓心和一個半徑。M-Tree的分為兩類結點:內結點,葉結點。每個結點儲存0~M個孩子結點。每個結點的多個屬性我們用一個entry類來組織,不同型別的結點entry類是不同的:
內節點:它是包含了
- entry ( O r ) = [ O r , ptr ( T ( O r ) ) , r ( O r ) , d ( O r , P ( O r ) ) ] \operatorname{entry}\left(O_{r}\right)=\left[O_{r}, \operatorname{ptr}\left(T\left(O_{r}\right)\right), r\left(O_{r}\right), d\left(O_{r}, P\left(O_{r}\right)\right)\right] entry(Or)=[Or,ptr(T(Or)),r(Or),d(Or,P(Or))]
O r O_r Or是結點圓心值, d ( O j , P ( O j ) ) d\left(O_{j}, P\left(O_{j}\right)\right) d(Oj,P(Oj))是當前結點圓心值到父節點圓心值的距離, r ( O r ) r(O_r) r(Or)是圓的半徑, p t r ( T ( O r ) ) ptr \left(T\left(O_{r}\right)\right) ptr(T(Or))是指向孩子結點的指標。
葉結點:
- entry ( O j ) = [ O j , oid ( O j ) , d ( O j , P ( O j ) ) ] \text { entry }\left(O_{j}\right)=\left[O_{j}, \text { oid }\left(O_{j}\right), d\left(O_{j}, P\left(O_{j}\right)\right)\right] entry (Oj)=[Oj, oid (Oj),d(Oj,P(Oj))]
O j O_j Oj是目標的值, d ( O j , P ( O j ) ) d\left(O_{j}, P\left(O_{j}\right)\right) d(Oj,P(Oj))是當前結點值和父節點值的距離, o i d ( O j ) oid(O_j) oid(Oj)是葉節點儲存的資料在資料集中的id或指標,
M-Tree的插入
在這一節描述M-tree的插入過程。插入過程需要將一個資料插入到對應的葉節點當中(同樣地,葉節點只能存放0-M個資料)。
從根節點開始,找到插入資料對應的葉節點有以下關鍵思路:
找到正確的孩子結點(最小範圍擴大):
- 計算每個孩子結點的圓心值和插入資料的距離dist
- 如果存在dist小於某些孩子結點的半徑(這意味著插入帶點不用擴大結點的半徑),則選擇最小dist對應的孩子結點。
- 若不存在,選擇dist減去半徑最小的孩子結點作為結果
葉節點開始分裂
- 到達葉節點後,若葉節點未滿,則直接將資料插入
- 若葉節點滿了,則需要將結點進行分裂。
M-Tree的分裂
對於一個滿的結點,我們需要將它分裂成兩個結點。然後
- 孩子結點按一定方式分配到兩個結點中
- 修改父節點的指標,使其指向兩個結點
下面我以下面的程式碼舉例分析
- 生成兩個新結點(行6-7)
- 儲存原結點的全部孩子結點和帶插入結點到陣列entrys中
- 在陣列entrys中選擇兩個結點,將其屬性更新到兩個新結點。選擇結點有很多中方式,比如最小範圍覆蓋,隨機等 (行13)
- 將陣列entrys中的全部結點劃分到兩個新結點中 (行16)
- 如果當前節點不是葉節點:
- 修改新結點和父節點的關係(行39-42)
- 將其中新結點1的全部屬性劃分到當前結點中,刪除新結點1(行44和48,注意45,46行是PM-Tree的內容,和本文無關)
- 如果父節點未滿,則將新結點二插入到父節點中(行54)
- 如果父節點滿,向上分裂父節點(行50-52)
- 如果當前節點不是葉節點:類似處理
1 void PM_Tree::Split(M_Node_St ** cur_node_ptr_address_, M_Node_St ** insert_node_address_)
2 {
3 M_Node_St * cur_node_ = *cur_node_ptr_address_;
4 M_Node_St * insert_node_ = *insert_node_address_;
5 /*spilt two new nodes */
6 M_Node_St* new_Mnode_1 = new M_Node_St(nullptr, 0, -1, -1);
7 M_Node_St* new_Mnode_2 = new M_Node_St(nullptr, 0, -1, -1);
8
9 vector<M_Node_St*> entries = cur_node_->ptr_sub_tree;
10 entries.emplace_back(insert_node_);
11
12 /*decide featuer value of node_1 and node_2 basing on entries*/
13 Promote(entries, new_Mnode_1, new_Mnode_2);
14
15 /*divide ertries into node_1 and node_2 */
16 Partition(entries, new_Mnode_1, new_Mnode_2);
17
18 if (Is_Root_Node(cur_node_)) {
19 M_Node_St* new_root = new M_Node_St(nullptr,0,-1,-1);
20 new_root->feature_val = new_Mnode_1->feature_val;/*update root feature value*/
21
22 /*update information*/
23 new_Mnode_1->patent_node = new_root;
24 new_Mnode_1->dist_to_parent = Cal_Dist_To_Parent(new_Mnode_1);
25 new_Mnode_2->patent_node = new_root;
26 new_Mnode_2->dist_to_parent = Cal_Dist_To_Parent(new_Mnode_2);
27 Merge_subNode_HR(new_Mnode_1);
28 Merge_subNode_HR(new_Mnode_2);
29
30
31 new_root->ptr_sub_tree.emplace_back(new_Mnode_1);
32 new_root->ptr_sub_tree.emplace_back(new_Mnode_2);
33 new_root->range = Cal_Cover_Radius(new_root);
34 Merge_subNode_HR(new_root);
35 root = new_root;
36 }
37 else {
38 /*update information*/
39 new_Mnode_1->patent_node = cur_node_->patent_node;
40 new_Mnode_1->dist_to_parent = Cal_Dist_To_Parent(new_Mnode_1);
41 new_Mnode_2->patent_node = cur_node_->patent_node;
42 new_Mnode_2->dist_to_parent = Cal_Dist_To_Parent(new_Mnode_2);
43 /*將new_Mnode_1中元素替換cur_node_元素,並更新父節點*/
44 assign_Node_All_Value(cur_node_, new_Mnode_1);
45 Merge_subNode_HR(cur_node_);
46 Merge_subNode_HR(new_Mnode_2);
47 /*delete memory*/
48 delete(new_Mnode_1);
49
50 if (Is_Full(new_Mnode_2->patent_node)) {
51 Split(&(new_Mnode_2->patent_node), &new_Mnode_2);
52 }
53 else {
54 new_Mnode_2->patent_node->ptr_sub_tree.emplace_back(new_Mnode_2);
55 }
56 }
57}
58
使用M-Tree進行range搜尋
M-Tree的程式碼
很早以前寫的程式碼,大致上的PM-tree是沒有問題的。在程式設計上存在血多瑕疵,希望大家多多指點
https://github.com/duoyw/PM-Tree
相關文章
- 一文了解cookieCookie
- 一文了解MysqlMySql
- 一文了解java列舉Java
- 一文了解增長黑客黑客
- 一文了解:Redis事務Redis
- 一文了解Text-to-SQLSQL
- 一文了解Flink State Backends
- 一文了解HAProxy主要特性
- 一文了解Dart語法Dart
- 一文了解DNS(域名系統)DNS
- 一文了解 DataLeap 中的 Notebook
- 一文了解什麼是PostgreSQLSQL
- 一文了解Docker基本概念Docker
- 一文了解Promise使用與實現Promise
- 一文了解:Redis的AOF持久化Redis持久化
- 一文了解有趣的位運算(&、|、^、~、>>、<<)
- 一文了解SAP IBP是什麼
- 一文了解Spring Boot啟動類SpringApplicationSpring BootAPP
- 快速加入Health Kit,一文了解稽核流程
- 一文了解主流大資料ETL工具大資料
- 一文了解 Java 中的構造器Java
- 一文了解 NebulaGraph 上的 Spark 專案Spark
- 一文了解萬用字元SSL證書字元
- 一文了解怎麼獲取代理IP
- 一文了解授信審批策略及流程
- 一文了解前端與全棧工程師!前端全棧工程師
- 一文了解Python反射機制(很詳細)Python反射
- 一文了解CDN(內容分發網路)
- 一文了解微服務的流程和組織微服務
- 一文了解 Nebula Graph 上的 Spark 專案Spark
- Java14版本特性【一文了解】Java
- 一文了解Android中路由(Router)的實現Android路由
- Java 22正式釋出,一文了解全部新特性Java
- 一文了解MySQL中的多版本併發控制MySql
- 一文了解主流線上公共 MQTT 伺服器MQQT伺服器
- 一文了解點晴PMS碼頭管理系統
- Linux C++ 開發5 - 一文了解CMake構建LinuxC++
- AI - 一文了解AIOps的含義、特點與功用AI