python2.7
import numpy as np
構建Kd樹:
KD樹的構造
一維的二叉查找樹很好構造,先對所有數據排序,然後每次取中值,把數據分成兩半,左半爲左子樹,右半爲右子樹;然後遞歸下去就好了。這樣可以保證構造出來的二叉樹是平衡的。
KD樹處理的數據是多維的,因此每次劃分需要選定某一維作爲參考來劃分數據。選定後所有數據按這一維排序,然後劃分成左子樹,右子樹。參考維度的選定可以依次選,比如這一層以X維劃分,下一層就以Y維,如此循環反覆。更好的方法是每次選擇方差最大的那一維。只要劃分以後左右區域都還有數據,劃分就進行下去,直到按某個節點劃分完以後兩邊沒有數據點爲止。
# kd-tree每個結點中主要包含的數據結構如下
class KdNode(object):
def __init__(self, dom_elt, split, left, right):
self.dom_elt = dom_elt # k維向量節點(k維空間中的一個樣本點)
self.split = split # 整數(進行分割維度的序號)
self.left = left # 該結點分割超平面左子空間構成的kd-tree
self.right = right # 該結點分割超平面右子空間構成的kd-tree
class KdTree(object):
def __init__(self, data):
k = len(data[0]) # 數據維度
def CreateNode(split, data_set): # 按第split維劃分數據集exset創建KdNode
if not data_set: # 數據集爲空
return None
# key參數的值爲一個函數,此函數只有一個參數且返回一個值用來進行比較
# operator模塊提供的itemgetter函數用於獲取對象的哪些維的數據,參數爲需要獲取的數據在對象中的序號
# data_set.sort(key=itemgetter(split)) # 按要進行分割的那一維數據排序
data_set.sort(key=lambda x: x[split])
split_pos = len(data_set) // 2 # //爲Python中的整數除法
median = data_set[split_pos] # 中位數分割點
split_next = (split + 1) % k # cycle coordinates
# 遞歸的創建kd樹
return KdNode(median, split,
CreateNode(split_next, data_set[:split_pos]), # 創建左子樹
CreateNode(split_next, data_set[split_pos + 1:])) # 創建右子樹
self.root = CreateNode(0, data) # 從第0維分量開始構建kd樹,返回根節點
BBF查詢:
#kdTree_bbf
class Prioritylist(object):
def __init__(self,kdnode,priority):
self.node=kdnode
self.priority=priority
prioritylist=[]#存放Prioritylist p1
def InsertPriorityList(kdnode,priority):
p1=Prioritylist(kdnode,priority)
if len(prioritylist)==0:
prioritylist.append(p1)
return
for i in range(len(prioritylist)):
if prioritylist[i].priority>=priority:
prioritylist.insert(i,p1)
break
else:
prioritylist.append(p1)
break
def RemovePriority(kdnode):
for i in range(len(prioritylist)):
if prioritylist[i].node.dom_elt==kdnode.dom_elt:
prioritylist.pop(i)
break
#優先級的計算,計算目標點和分割點之間的距離(某一維度),即優先級
def CalPriority(kdnode,target,split):
return abs(kdnode.dom_elt[split]-target[split])
def CalDistance(vector1,vector2):
return ((np.array(vector1)-np.array(vector2))**2).sum()**0.5
def BBFFindNearest(kdnode,target):
nearest=kdnode
sec_near=float("inf")
priority=CalPriority(nearest,target,kdnode.split)
InsertPriorityList(nearest,priority)
top_node=None
currentNode=None
fir_dis=CalDistance(nearest.dom_elt,target)
sec_dis=0
while len(prioritylist)>0:
top_node=prioritylist[0].node
RemovePriority(top_node)
while top_node!=None:
if top_node.left!=None or top_node.right!=None:
split=top_node.split
if target[split]<=top_node.dom_elt[split]:
if top_node.right!=None:
priority=CalPriority(top_node.right,target,top_node.split)
InsertPriorityList(top_node.right,priority)
top_node=top_node.left
else:
if top_node.left!=None:
priority=CalPriority(top_node.left,target,top_node.split)
InsertPriorityList(top_node.left,priority)
top_node=top_node.right
currentNode=top_node
else:
currentNode=top_node
top_node=None
if currentNode!=None and (CalDistance(nearest.dom_elt,target)>CalDistance(currentNode.dom_elt,target)):
sec_near=nearest
nearest=currentNode
fir_dis = CalDistance(nearest.dom_elt, target)
sec_dis=CalDistance(sec_near.dom_elt,target)
elif currentNode!=None and (CalDistance(nearest.dom_elt,target)<CalDistance(currentNode.dom_elt,target)):
if sec_near==float("inf"):
sec_near=currentNode
sec_dis = CalDistance(sec_near.dom_elt, target)
else:
if CalDistance(sec_near.dom_elt,target)>CalDistance(currentNode.dom_elt,target):
sec_near=currentNode
sec_dis=CalDistance(sec_near.dom_elt,target)
return nearest,sec_near,fir_dis,sec_dis
測試:
# KDTree的前序遍歷
def preorder(root):
print root.dom_elt
if root.left: # 節點不爲空
preorder(root.left)
if root.right:
preorder(root.right)
if __name__=='__main__':
data=[[2,3],[5,4],[9,6],[4,7],[8,1],[7,2]]
kd=KdTree(data)
preorder(kd.root)
nearest,sec_near=BBFFindNearest(kd.root,[0,0])
print 'The nearest point is:',nearest.dom_elt,',Distance is:',firdis
print 'The second point is:',sec_near.dom_elt,',Distance is:',secdis
測試結果: