求3D点集中最近点的一个空间二叉树实现
HouSisong@GMail.com 2010.09.12
(2011-03-04 修改节点内存泄漏,漏了一句m_nodePool.push_back(m_cur); )
tag:求3D点集中最近点,空间二叉树,点集最小包围球
摘要:
这几天为了解决一个颜色空间匹配搜索速度太慢的问题(求3D点集中最近点),写了一个基于3D空间二叉树分割的算法的实现,
速度提高了几十倍;
正文:
A: 问题简单描述
已知3D点集U(较多),求出U中与给定点p最近的点;可能会非常多次 的给定不同的p点,而点集U不变;
B: 一些基础代码和最简单的一个实现和其简要优化
定义3D点:
寻找最近点的算法简单实现:
该算法遍历点集U(points[]),找寻点集中距离test_point最近的点,返回其坐标,算法复杂度为O(N);
简单优化:
前面的程序计算点之间的距离来比较大小,涉及到开方运算,比较慢;而比较的时候,改成比较距离的平方效果也一样,
函数返回距离的时候,在把最终结果开方就好了;代码如下:
这个代码的速度是前面实现的3倍多;
当然该算法还可以继续优化的;但因为算法复杂度太高,进一步优化的意义不大;
下面给出一个平均复杂度O(log(N))的算法
C: 基于3D空间二叉树分割的算法的实现
对点集U进行预处理,生成一棵3D空间二叉树:
将点集U的最长轴按中值划分成2个新的点集;计算新形成的2个点集的最小包围球;
如果新划分出的点集的点个数比较多,可以进一步划分,一直到不用划分;
(划分方法可以是该轴的中值、均值或平分个数的值等,程序里选择用中值划分;
包围体也可以为其它3D几何包围体,比如正6面体等,程序里选择球型包围体,算法实现更简单快速些)
利用二叉树寻找离给定点最近的点:
程序用深度优先顺序遍历二叉树,遇到有2分支的时候优先处理距离近的分支;
快速排除一个点集的算法原理: 假设搜寻过程中,当前已经得到的离p点最近的点的距离为minD,
现在寻找到了点集u(包围球半径ur,球心坐标up); 如果 (p到up的距离 - ur) > minD ,那么该点集u和
其子点集都可以立即排除掉了;而且该判断表达式还可以改进为 p到up的距离的平方>(minD+ur)的平方
计算起来就更加快速了;
(如果想用广度优先算法遍历二叉树,可以维持一个队列,储存当前没有被排除的点集,然后展开这些点集,快速排除不可能的点集,直到没有点集分
支可以展开;这种遍历方式可以利用的快速排除一个点集的算法原理为: 假设搜寻过程中,当前已经得
到的离p点最近的点集距离为minD,该点集包围球半径为ur,现在寻找到了点集v(包围球半径vr,球心坐
标vp); 如果 (p到vp的距离 - mr) > (minD+ur),那么该点集v和其子点集就可以立即排除掉;该判断表
达式也可以改进为 p到vp的距离的平方 > (minR+ur+mr)的平方 ! )
代码实现:
D:完整测试代码
E:速度对比
使用的编译器为vc2008;测试使用的CPU为i7 920;
--------------------------------------------
3D点集数量: 64
遍历算法0: 每秒处理1134824个点
遍历算法1: 每秒处理3564980个点
空间2叉树算法: 每秒处理4871770个点
--------------------------------------------
3D点集数量: 256
遍历算法0: 每秒处理291841个点
遍历算法1: 每秒处理1059374个点
空间2叉树算法: 每秒处理3144091个点
--------------------------------------------
3D点集数量: 1000
遍历算法0: 每秒处理76064个点
遍历算法1: 每秒处理295625个点
空间2叉树算法: 每秒处理2068754个点
--------------------------------------------
3D点集数量: 10000
遍历算法0: 每秒处理7643个点
遍历算法1: 每秒处理30166个点
空间2叉树算法: 每秒处理1111412个点
--------------------------------------------
3D点集数量: 100000
遍历算法0: 每秒处理772个点
遍历算法1: 每秒处理2984个点
空间2叉树算法: 每秒处理604217个点
--------------------------------------------
3D点集数量: 1000000
遍历算法0: 每秒处理83个点
遍历算法1: 每秒处理272个点
空间2叉树算法: 每秒处理272924个点