Created
March 20, 2019 08:45
-
-
Save ZJUGuoShuai/4712dae7fc941d6ce350a61a86baa08f to your computer and use it in GitHub Desktop.
自己实现的 KD-Tree 类,包含求最近邻的方法。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| from collections import namedtuple | |
| from operator import itemgetter | |
| from pprint import pformat | |
| import numpy as np | |
| class Node(namedtuple('Node', 'location left_child right_child')): | |
| def __repr__(self): | |
| return pformat(tuple(self)) | |
| class KDTree(): | |
| def __init__(self, points): | |
| self.tree = self._make_kdtree(points) | |
| if len(points) > 0: | |
| self.k = len(points[0]) | |
| else: | |
| self.k = None | |
| def _make_kdtree(self, points, depth=0): | |
| if not points: | |
| return None | |
| k = len(points[0]) | |
| axis = depth % k | |
| points.sort(key=itemgetter(axis)) | |
| median = len(points) // 2 | |
| return Node( | |
| location=points[median], | |
| left_child=self._make_kdtree(points[:median], depth + 1), | |
| right_child=self._make_kdtree(points[median + 1:], depth + 1)) | |
| def find_nearest(self, | |
| point, | |
| root=None, | |
| axis=0, | |
| dist_func=lambda x, y: np.linalg.norm(x - y)): | |
| if root is None: | |
| root = self.tree | |
| self._best = None | |
| # 若不是叶节点,则继续向下走 | |
| if root.left_child or root.right_child: | |
| new_axis = (axis + 1) % self.k | |
| if point[axis] < root.location[axis] and root.left_child: | |
| self.find_nearest(point, root.left_child, new_axis) | |
| elif root.right_child: | |
| self.find_nearest(point, root.right_child, new_axis) | |
| # 回溯:尝试更新 best | |
| dist = dist_func(root.location, point) | |
| if self._best is None or dist < self._best[0]: | |
| self._best = (dist, root.location) | |
| # 若超球与另一边超矩形相交 | |
| if abs(point[axis] - root.location[axis]) < self._best[0]: | |
| new_axis = (axis + 1) % self.k | |
| if root.left_child and point[axis] >= root.location[axis]: | |
| self.find_nearest(point, root.left_child, new_axis) | |
| elif root.right_child and point[axis] < root.location[axis]: | |
| self.find_nearest(point, root.right_child, new_axis) | |
| return self._best |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment