BSP树的简单理解和二叉树空间索引思路的详解

BSP树

二叉空间分区树(Binary Space Partioning Tree,BSP Tree)也叫二叉空间分割树,简称BSP树

img

img

从多边形列表生成BSP树的算法

  • 从列表中选择一个多边形 P。
  • 在BSP树中创建一个节点N,并将P添加到该节点的多边形列表中。
  • 对于列表中的其他多边形:
    • 如果该多边形完全位于包含 P 的平面前面,请将该多边形移动到 P 前面的节点列表中。
    • 如果该多边形完全位于包含 P 的平面后面,请将该多边形移动到 P 后面的节点列表中。
    • 如果该多边形与包含 P 的平面相交,则将其拆分为两个多边形,并将它们移动到 P 后面和前面的相应多边形列表中。
    • 如果该多边形位于包含 P 的平面中,请将其添加到节点 N 处的多边形列表中。
  • 将此算法应用于 P 前面的多边形列表。
  • 将此算法应用于 P 后面的多边形列表。

BSP的缺点

  • 生成 BSP 树可能非常耗时。
  • BSP不能解决可见表面测定的问题。

BSP的用途

  • 它用于3D视频游戏和机器人中的碰撞检测。

  • 它用于光线追踪

  • 它涉及复杂空间场景的处理。

    k-d树

    二叉树空间索引思路

    直觉

    给定一堆已有的样本数据,和一个被询问的数据点(红色五角星),我们如何找到离五角星最近的15个点?
    1.png

    先忽略在编程上的实现,想一想一个人如何主观地执行。嗯,他一定会把在五角附近的一些点中,分别计算每一个的距离,然后选最近的15个。这样可能只需要进行二三十次距离计算,而不是300次。
    2.png

    如图,只对紫圈里的点进行计算。

    啊哈!问题来了。我们讲到的“附近”已经包含了距离的概念,如果不经过计算我们怎么知道哪个点是在五角星的“附近”?为什么我们一下就认出了“附近”而计算机做不到?那是因为我们在观看这张图片时,得到的输入是已经带有距离概念的影像,然而计算机在进行计算时得到的则是没有距离概念的坐标数据。如果要让一个人人为地从300组坐标里选出最近的15个,而不给他图像,那么他也省不了功夫,必须要把300个全部计算一遍才行。

    这样来说,我们要做的就是在干巴巴的坐标数据上进行加工,将空间分割成小块,并以合理地方法将信息进行储存,这样方便我们读取“附近”的点。

    切割

    这只危险的兔子,它又回来了!它今天上了四个纹身,爱心、月牙、星星和眼泪,下面是它的照片。
    3.jpg

    我们来回答一个简单的问题:在这幅照片上,距离爱心最近的纹身是什么?记得上一篇文章中,我们选用的特征是每一只兔子的身高和体重;这次就不一样了,在这个问题中,每个纹身的特征是照片平面上的横轴和竖轴的坐标。

    对于这个问题,如果进行蛮算的办法我们需要计算 3 次距离(分别和月亮、眼泪和星星算一次)。下面我们要做的是把整个空间按照左右和上下进行等分,并且把分割后的小空间以二叉树形式进行记录,这样可以很快地读取邻近的点而省去计算量。

    好,我们先竖向沿中间把这个兔子切成两半
    4.jpg

    再沿横向从中间切成四份
    5.jpg

    再沿着竖向平分八份
    6.jpg

    最后再沿横向切一次。这次有些区域是完全空白的,我们就把它舍弃不要了,得到 14 份:
    7.jpg

    我们再按照上下左右的关系把切开的图片做成一个二叉树,树的每一个节点是一幅图,它的两个枝是这幅图平分出来的子图。
    8.jpg

    可以看出这个树状结构包含了很多局部性的信息,因为它的每一个节点都是按照上下或者左右进行平分的,因此如果两个点在树中的距离较近,那么它们的实际距离就是比较近的。

    搜寻

    接下来我们要通过这棵二叉树找到离爱心最近的纹身。

    首先从树的最顶端开始,向下搜寻找到最底部包含爱心的节点。这个操作非常简单,因为每一次分割要么是沿着某纵线 x=a要么是沿着横线 y=a,因此只需要判断爱心的 x或 y轴坐标是大于 a还是小于 a,便知道是向左还是右边选择树枝。
    9.jpg

    在找到了爱心之后,我们沿着相同的路径向上攀爬。只爬了一节就发现了屁股上的两个纹身
    10.png

    这里看出,在8平分的情况下,爱心和月亮是在同一个区域的。在某种意义上来讲它们是“近”的,但是我们还不能确定它们是最近的,因此还要继续向上攀爬寻找。再继续向上爬两个节点,都没有出现爱心和月亮以外的纹身。在下面这个节点中
    11.png

    我们发现爱心和月亮之间的距离(红线)要小于爱心和分割线的距离(蓝线),也就是说,不论分割线的右边是什么情况,那边的纹身都不可能离爱心更近。因此可以判断,离爱心最近的图形是月亮。

    这样,我们只计算了一次爱心和月亮之间的距离和一次爱心和分割线之间的距离,而不是分别计算爱心和其他三个纹身的距离。并且,要知道,爱心和分割线之间距离的计算非常简单,就是爱心的 x 坐标和分割线的 x 坐标的差(的绝对值),相比于计算两点之间的距离要省下很多计算量。

    麻烦

    啊,但也有可能这个搜寻最近点的过程没那么顺利。在上面的计算中,在找到了离爱心比较近的月亮之后,我们发现爱心距离分割线的距离比较远,因此确定月亮的确就是最近的。但是,在分割线的另一边有一个更近的纹身,那么情况就稍微复杂了。

    就说这个兔子啊,又去加了两个纹身,一片叶子和一个圆圈。
    12.png

    二叉树分割上也相应地多出这两个纹身。我们想找到离爱心最近的纹身,所以依旧向下搜寻先找到爱心。
    13.jpg

    我们找来一张纸,记下在已访问节点中距离爱心最近的纹身和所对应的距离。现在这张纸还是空的。
    14.png

    向上爬了一节,发现那一节的另一个枝里有月亮,于是跑下去查看月亮的坐标,计算爱心和月亮的距离,并在纸上记录 (图形=月亮,距离=d1)(图形=月亮,距离=�1)。

    再回到蓝圈的节点向上爬,继续向上爬。我们发现,d1�1(红线)大于爱心和分割线的距离(蓝线)。
    new1.png

    也就是说分割线的另一边可能有更近的点,所以从另一个分枝开始向下搜,找到…
    16.png

    在另一个分枝中我们追溯到圆圈,并计算它与爱心的距离 d2,发现 d2>d1,比月亮远,所以丢弃不要。

    再向上爬一个节,我们发现 d1(红线)大于爱心和切分线之间的距离(蓝线)
    new2.png

    因此,切分线的另一端可能有更近的纹身,因此我们从另一个树枝向下搜索…
    18.png

    找到了叶子。(所幸在这个分枝里只搜索到了叶子,如果有更多的图形的话,还需要进行多层的递归。具体的过程会在后面的详细篇中讲解。)计算叶子和爱心之间的距离,得 d3,并发现 d3<d1,比月亮更近,于是更新纸上的记录为 (纹身=叶子,距离=d3)。

    再向上攀登一节,我们发现 d3小于爱心和切分线的距离,因此另一边的数据就不用考虑了。
    new3.png

    这次我们已经爬到了树的最顶端,完成了搜索,纸上记载的 (叶子,d3) 就是最近的纹身和对应的距离。
    20.png

    结语

    在以上的算法中,当我们已经找到了比切分线更近的点时,就不需要继续搜索切分线另一边的点了,因为那些只会更远。于是,通过把整个空间进行分割并以树状结构进行记录,我们只需要在问题点附近的一些区域进行搜寻便可以找到最近的数据点,节省了大量的计算。

    参考[【量化课堂】kd 树算法之思路篇 - JoinQuant量化课堂 - JoinQuant]

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值