KD-Tree 的一些理解--

2024-08-22 18:12
文章标签 理解 tree kd

本文主要是介绍KD-Tree 的一些理解--,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

阅读文档:Wikipedia 上的 KD Tree 。
https://en.wikipedia.org/wiki/K-d_tree

引言

In computer science, a k-d tree (short for k-dimensional tree) is a space-partitioning data structure for organizing points in a k-dimensional space. K-dimensional is that which concerns exactly k orthogonal axes or a space of any number of dimensions.[1]
K-D Tree即是K-Dimensional (维度)Tree 的缩写。K-D Tree主要用于空间划分,K-D也就是说在k维空间中对于一些点的划分。
关于这里的K-D(dimensional),文档专门为此做出了解释,k维,指的是严格意义上的k个正交轴或任何数量维度的空间。(在这里,我觉得正交轴更好理解,主要是理解代数上的正交的意思即可。)
k-d trees are a useful data structure for several applications, such as:Searches involving a multidimensional search key (e.g. range searches and nearest neighbor searches) & Creating point clouds. k-d trees are a special case of binary space partitioning trees.
K-D树对于一些应用,是一种非常有用的数据结构。

概述

The k-d tree is a binary tree in which every node is a k-dimensional point.[2]
☆这一句说明了重点。
k-d 树是一个二叉树,并且对于树上的每一个节点都是k维的点。
Every non-leaf node can be thought of as implicitly generating a splitting hyperplane that divides the space into two parts, known as half-spaces.
每一个非叶子节点,都可以认为是可以划分生成一个划分超平面。这个超平面可以将这个空间分为两个部分。

主要是理解hyperplane
hyperplane指的是一个维度比整个空间少1的超平面。
在二维平面上,hyperplane就是一条直线。
在三维空间中,hyperplane就是一个平面。
在n维空间中,hyperplane是一个n-1维的超平面。

Points to the left of this hyperplane are represented by the left subtree of that node and points to the right of the hyperplane are represented by the right subtree.
位于超平面左侧的点都由该节点的左子树所表示。而位于超平面右侧的点都由该节点的右子树所表示。

The hyperplane direction is chosen in the following way: every node in the tree is associated with one of the k dimensions, with the hyperplane perpendicular to that dimension's axis.
这段话介绍了超平面方向选取的方法。
这棵树上的每一个节点都是与k维度的一个维度相关联,
在该节点上,构建的超平面是垂直于该维度对应的坐标轴的。

So, for example, if for a particular split the "x" axis is chosen, all points in the subtree with a smaller "x" value than the node will appear in the left subtree and all points with a larger "x" value will be in the right subtree. In such a case, the hyperplane would be set by the x value of the point, and its normal would be the unit x-axis.[3]
对于上面的一个举例:
基于x的值构建一个超平面:对于一个特定的划分,特定的一个x被选取,对于所有的点,如果它们的x值小于“x”,则位这个节点的左子树,反之,则右子树。
在这个例子中,超平面的设置是由点的x坐标值来决定的,而它的法向量就是单位x轴

K-D 树的构造方法

Since there are many possible ways to choose axis-aligned splitting planes, there are many different ways to construct k-d trees. The canonical method of k-d tree construction has the following constraints:[4]
因为存在许多选择轴对齐分割平面的方式,因此可以以许多不同的方式构建 k-d 树。k-d 树构建的标准方法有以下约束条件:

As one moves down the tree, one cycles through the axes used to select the splitting planes.
当沿着树向下移动时,用于选择分割平面的轴会循环使用
(For example, in a 3-dimensional tree, the root would have an x-aligned plane, the root's children would both have y-aligned planes, the root's grandchildren would all have z-aligned planes, the root's great-grandchildren would all have x-aligned planes, the root's great-great-grandchildren would all have y-aligned planes, and so on.)
☆例如,在一个三维树中:
根节点的分割平面是沿着 x 轴对齐的。
根节点的子节点的分割平面都是沿着 y 轴对齐的。
根节点的孙节点的分割平面都是沿着 z 轴对齐的。
根节点的曾孙节点的分割平面都是沿着 x 轴对齐的。
根节点的曾曾孙节点的分割平面都是沿着 y 轴对齐的。
这种交替的平面分割方式一直延续下去。
Points are inserted by selecting the median of the points being put into the subtree, with respect to their coordinates in the axis being used to create the splitting plane. (Note the assumption that we feed the entire set of n points into the algorithm up-front.)
这段话描述了,在k维空间中如何插入一组点。
Note:我们假设我们一开始对于这个树就有n个点。
接着我们往这棵子树上插入一些点,会根据该子树所使用的分割轴(x、y或z轴)的坐标,选择这组点的中位数作为新的分割点插入到该子树中。

(也就是说,当我们需要将一组新的点插入到当前的kd树子树中时,我们不是直接将整个点集插入进去。而是先计算这组新点在当前分割轴上的中位数,然后将这个中位数作为一个新的分割点,插入到当前子树的适当位置。
也就是说,我们是将这组新点的中位数作为一个新的分割点插入进去,而不是直接将整个点集插入。这样可以保持kd树的平衡性。)

这样说有点绕,我们的借助Claude3.5来进一步理解。

This method leads to a balanced k-d tree, in which each leaf node is approximately the same distance from the root. However, balanced trees are not necessarily optimal for all applications.

这种方法可以生成一个平衡的 kd 树,其中每个叶子节点离根节点的距离大致相同。然而,平衡的树并不一定对所有应用场景都是最优的。
Note that it is not required to select the median point. In the case where median points are not selected, there is no guarantee that the tree will be balanced. To avoid coding a complex O(n) median-finding algorithm[5][6] or using an O(nlogn) sort such as heapsort or mergesort to sort all n points, a popular practice is to sort a fixed number of randomly selected points, and use the median of those points to serve as the splitting plane. In practice, this technique often results in nicely balanced trees.
注意到,我们没有必要选择中位数。当我们不选择中位数的情况下,我们无法保证树的平衡好。
为了避免编写复杂的 O(n) 中位数查找算法或使用 O(nlogn) 的排序算法(如堆排序或归并排序)来对所有 n 个点进行排序,一种常见的做法是:
☆对一个固定数量的随机选择的点进行排序,然后使用这些点的中位数作为分割平面。
在实际应用中,这种技术通常可以生成非常平衡的 kd 树。

Given a list of n points, the following algorithm uses a median-finding sort to construct a balanced k-d tree containing those points.
给定一个包含 n 个点的列表,下面的算法使用中位数查找排序来构建一棵包含这些点的平衡 k-d 树。

It is common that points "after" the median include only the ones that are strictly greater than the median in the current dimension. For points that lie on the median in the current dimension, it is possible to define a function that compares them in all dimensions. In some cases, it is acceptable to let points equal to the median lie on one side of the median, for example, by splitting the points into a "lesser than" subset and a "greater than or equal to" subset.
这段话主要是说明了如何处理在某一坐标轴上严格上等于中位数的点的方法。
以下是对于中位数之后的点的定义以及处理在当前维度上等于中位数的点两种方法。

通常情况下,中位数"之后"的点仅包括当前维度中严格大于中位数的点。对于在当前维度上与中位数相等的点,可以定义一个函数来比较它们在所有维度上的大小。在某些情况下,将等于中位数的点划分到中位数的一侧是可以接受的,例如将点划分为"小于"子集和"大于或等于"子集。

This algorithm creates the invariant that for any node, all the nodes in the left subtree are on one side of a splitting plane, and all the nodes in the right subtree are on the other side. Points that lie on the splitting plane may appear on either side. The splitting plane of a node goes through the point associated with that node (referred to in the code as node.location).
这段话,是对于k-d树的一个评价。

这个算法创造了一个不变性,即对于任何节点,左子树中的所有节点都在一个分割平面的一侧,而右子树中的所有节点都在另一侧。位于分割平面上的点可以出现在任何一侧。节点的分割平面通过与该节点相关联的点(在代码中称为node.location)。

Alternative algorithms for building a balanced k-d tree presort the data prior to building the tree. Then, they maintain the order of the presort during tree construction and hence eliminate the costly step of finding the median at each level of subdivision. Two such algorithms build a balanced k-d tree to sort triangles in order to improve the execution time of ray tracing for three-dimensional computer graphics. These algorithms presort n triangles prior to building the k-d tree, then build the tree in O(nlogn) time in the best case.[7][8] An algorithm that builds a balanced k-d tree to sort points has a worst-case complexity of O(knlogn).[9][10] This algorithm presorts n points in each of k dimensions using an O(nlogn) sort such as Heapsort or Mergesort prior to building the tree. It then maintains the order of these k presorts during tree construction and thereby avoids finding the median at each level of subdivision.

这段话,说明了如何理解提高k-d树的效率的方法比较。
有一些构建平衡 k-d 树的替代算法会先对数据进行预排序,然后在构建树的过程中保持这个预排序的顺序,从而避免了在每个分割层级上寻找中位数的代价。
这类算法主要包括两种:

  1. 用于排序三维计算机图形中三角形的算法。这些算法会先对 n 个三角形进行预排序,然后在最佳情况下以 O(n log n) 的时间复杂度构建 k-d 树,以提高光线追踪的执行时间。
  2. 用于排序点的算法。这种算法的最坏情况时间复杂度为 O(k n log n),其中 k 是维度的数量。算法首先使用 O(n log n) 复杂度的排序算法(如堆排序或归并排序)对 n 个点在 k 个维度上进行预排序,然后在构建树的过程中保持这些预排序的顺序,从而避免了在每个分割层级上寻找中位数。

总之,这些算法通过预排序和在构建过程中保持排序顺序,避免了在每个分割层级上寻找中位数的开销,从而提高了构建平衡 k-d 树的效率。

我们借助Claude3.5 来举例理解一下k-d树为什么有时效率是低下的。

————————————————————————
关于k-d树,第一遍是看课,稍微懂了一些。阅读这个文档后,确实理解多了很多。不足的一些点就是没有结合一些图形写到博客上。

这篇关于KD-Tree 的一些理解--的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1097018

相关文章

认识、理解、分类——acm之搜索

普通搜索方法有两种:1、广度优先搜索;2、深度优先搜索; 更多搜索方法: 3、双向广度优先搜索; 4、启发式搜索(包括A*算法等); 搜索通常会用到的知识点:状态压缩(位压缩,利用hash思想压缩)。

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入(Embedding)方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节:嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以被看作是自然语言处理中的“翻译机”,它将文本中的单词或短语转换成计算机能够理解的数学形式,即向量。 正如翻译机将一种语言

【C++高阶】C++类型转换全攻略:深入理解并高效应用

📝个人主页🌹:Eternity._ ⏩收录专栏⏪:C++ “ 登神长阶 ” 🤡往期回顾🤡:C++ 智能指针 🌹🌹期待您的关注 🌹🌹 ❀C++的类型转换 📒1. C语言中的类型转换📚2. C++强制类型转换⛰️static_cast🌞reinterpret_cast⭐const_cast🍁dynamic_cast 📜3. C++强制类型转换的原因📝

深入理解RxJava:响应式编程的现代方式

在当今的软件开发世界中,异步编程和事件驱动的架构变得越来越重要。RxJava,作为响应式编程(Reactive Programming)的一个流行库,为Java和Android开发者提供了一种强大的方式来处理异步任务和事件流。本文将深入探讨RxJava的核心概念、优势以及如何在实际项目中应用它。 文章目录 💯 什么是RxJava?💯 响应式编程的优势💯 RxJava的核心概念

如何通俗理解注意力机制?

1、注意力机制(Attention Mechanism)是机器学习和深度学习中一种模拟人类注意力的方法,用于提高模型在处理大量信息时的效率和效果。通俗地理解,它就像是在一堆信息中找到最重要的部分,把注意力集中在这些关键点上,从而更好地完成任务。以下是几个简单的比喻来帮助理解注意力机制: 2、寻找重点:想象一下,你在阅读一篇文章的时候,有些段落特别重要,你会特别注意这些段落,反复阅读,而对其他部分

深入理解数据库的 4NF:多值依赖与消除数据异常

在数据库设计中, "范式" 是一个常常被提到的重要概念。许多初学者在学习数据库设计时,经常听到第一范式(1NF)、第二范式(2NF)、第三范式(3NF)以及 BCNF(Boyce-Codd范式)。这些范式都旨在通过消除数据冗余和异常来优化数据库结构。然而,当我们谈到 4NF(第四范式)时,事情变得更加复杂。本文将带你深入了解 多值依赖 和 4NF,帮助你在数据库设计中消除更高级别的异常。 什么是

分布式系统的个人理解小结

分布式系统:分的微小服务,以小而独立的业务为单位,形成子系统。 然后分布式系统中需要有统一的调用,形成大的聚合服务。 同时,微服务群,需要有交流(通讯,注册中心,同步,异步),有管理(监控,调度)。 对外服务,需要有控制的对外开发,安全网关。

Java IO 操作——个人理解

之前一直Java的IO操作一知半解。今天看到一个便文章觉得很有道理( 原文章),记录一下。 首先,理解Java的IO操作到底操作的什么内容,过程又是怎么样子。          数据来源的操作: 来源有文件,网络数据。使用File类和Sockets等。这里操作的是数据本身,1,0结构。    File file = new File("path");   字

理解java虚拟机内存收集

学习《深入理解Java虚拟机》时个人的理解笔记 1、为什么要去了解垃圾收集和内存回收技术? 当需要排查各种内存溢出、内存泄漏问题时,当垃圾收集成为系统达到更高并发量的瓶颈时,我们就必须对这些“自动化”的技术实施必要的监控和调节。 2、“哲学三问”内存收集 what?when?how? 那些内存需要回收?什么时候回收?如何回收? 这是一个整体的问题,确定了什么状态的内存可以

树(Tree)——《啊哈!算法》

树 什么是树?树是一种特殊的图,不包含回路的连通无向树。 正因为树有着“不包含回路”这个特点,所以树就被赋予了很多特性。 一棵树中的任意两个结点有且仅有唯一的一条路径连通。一棵树如果有n个结点,那么它一定恰好有n-1条边。在一棵树中加一条边将会构成一个回路。 一棵树有且只有一个根结点。 没有父结点的结点称为根结点(祖先)。没有子结点的结点称为叶结点。如果一个结点既不是根结点也不是叶