实验内容:求平面最近点对
实验步骤
- 实现最邻近点对,先定义了一个Point结构体类,结构中包含了横纵坐标
- 用srand算法随机生成若干点的横纵坐标
- 写两个快速排序函数Qsortx,Qsorty来实现对坐标x,y的排序,横纵坐标排序算法在分治算法之前进行处理,减少分治算法的时间复杂度。
- 分治算法:
第一类:只有一个点 return 最大距离
第二类:只有两个点,那么直接调用Distance()(计算两个点之间距离的函数),并把两个点记录下来;
第三类:如果点数为三个或以上,开始划分工作
对按照x排好序的点取出其中的中位数,按照中位数来进行左右划分(再开两个Point点集pts1和pts2)
递归调用分治算法函数,分别对左右两边的点集计算其最小距离,并记录形成最小距离的点
对比左右两边,取出最小的距离和点
以中位数横坐标为基准,将x坐标距离中位数的距离小于最小距离的点记录的在新点集中,并对该点集进行排序
对于任意一个在新集合中的点,假设它处于左半边与他相距小于最小距离的点只有右边宽为distance,长为2distance(y-distance,y+distance)。对该区域内所有点(不超过四个)进行挨个检验,该步骤时间复杂度为常数
关键函数:
//求出最近点对记录,并将两点记录再a、b中
float ClosestPair(Point points[], Point points1[], int length, Point& a, Point& b)
{
float distance; //记录集合points中最近两点距离
float d1, d2; //记录分割后两个子集中各自最小点对距离
int i = 0, j = 0, k = 0; //用于控制for循环的循环变量
Point a1, b1, a2, b2; //保存分割后两个子集中最小点对
if (length < 2)return NO_DISTANCE; //若子集长度小于2,定义为最大距离,表示不可达
if (length == 2)
{
a = points[0];
b = points[1];
distance = Distance(points[0], points[1]);
}
else
{
Point* pts1 = new Point[length]; //开辟两个子集
Point* pts2 = new Point[length];
float mid = points[(length - 1) / 2].x; //排完序后的中间下标值,即中位数
for (i = 0; i < length / 2; i++)
pts1[i] = points[i];
for (int j = 0, i = length / 2; i < length; i++)
pts2[j++] = points[i];
d1 = ClosestPair(pts1, points1, length / 2, a1, b1); //分治求解左半部分子集的最近点
d2 = ClosestPair(pts2, points1, length - length / 2, a2, b2); //分治求解右半部分子集的最近点
if (d1 < d2) { distance = d1; a = a1; b = b1; }
else { distance = d2; a = a2; b = b2; }
//求解跨分割线并在δ×2δ区间内的最近点对
//points1是已经按照纵坐标排好序的结构体数组,所以当把处于中间2d宽度的点放到pts3数组中时,pts3数组也是排好序的
Point* pts3 = new Point[length];
for (i = 0, k = 0; i < length; i++)
if (abs(points1[i].x - mid) <= distance)
pts3[k++] = points1[i];
for (i = 0; i < k; i++)
for (j = i + 1; j <= i + 5 && j < k; j++) //只需与有序的领接的的4个点进行比较
{
if (Distance(pts3[i], pts3[j]) < distance)
{//如果跨分割线的两点距离小于已知最小距离,则记录该距离
distance = Distance(pts3[i], pts3[j]);
a = pts3[i];
b = pts3[j];
}
}
}
return distance;
}
四个点分析:(参考论文:求平面点集最近点对的一个改进算法 周玉林)
只截取一部分图片,详情参考论文
本实验的改进之处为:
1. 对point点的纵坐标排序放在分治算法之前进行处理,减少分治算法的时间复杂度。
2. 对于可能存在的左边一点右边一点的最近点对的情况的检验中,由6个点改进为4个点,减少了计算量。