1,问题
最近对问题
2,解析
令P为笛卡尔平面上n>1个点构成的集合。简单起见,假设集合中每个点都不一样。我们还假设这些点是按照其x周坐标升序排列的。为了更加方便,我们还按照点的y轴坐标在另一个列表中进行升序排列,分别记为Px,Py。当2<=n<=3时,通过蛮力求解出D。当n>3时,可以利用点集在方向上的中位数mid,在该处作一条垂线,将点集分成大小分别为ceil(n/2) 和int(n/2)两个子集Px1和Px然后通过递归求解。但是,这样的D不一定是整个P中的最近对距离,因为距离最近的两个点可别位于中线mid的两侧。因此,在合并较小子问题的解时,需要检查是否存在这样的点。显然,我们可以在关注以分割带为对称的,宽度为2D的垂直带中的点,因为任何其它点对的距离都至少为D。是来自Py,位于分割线2D宽度范围内的垂直带的点的列表。由于Py的特点,因为S是按照y轴升序的。我们扫描该列表,当遇到更近的点对时,更新目前为止的D。初始情况下,Dmin=D,但接下来Dmin<D。设p(x,y)为列表中的点。如果另一个点p’(x’,y’)和p点的距离小于Dmin,那么p’一定在p的列表后面,并且两点在y轴上的距离一定小于Dmin。在几何上,这意味着p’点一定包含在以mid线为对称轴,长为2D,宽为D的矩形中。该算法主要原理利用了以下事实:矩形内一般只能包含少量候选点,因为每一边内,点与点的距离至少为D.其实很容易证明,在矩形内满足条件的点(包括p在内)的总数不超过8个,更细致的研究表明这个数不会大于6。也就是说,在移动下一个点之前,算法最多只需要考虑S中的下5个点。
3,设计
int EfficientClosestPair(Px,Py,pointnum,Dmin){
if n < 3 return 蛮力算法;
else {
将Px的前ceil(n/2)个点复制到Px1
后n/2个点复制到Px2
将Py的前ceil(n/2)个点复制到Py1
后n/2个点复制到Py2
Dmin1 = EfficientClosestPair(Px1,Py1);
Dmin2 = EfficientClosestPair(Px2,Py2);
Dmin = min(Dmin1,Dmin2);
S = Py中|x - mid|<Dmin 的点集
for (i from 0 to n){
for (j from i+1 to i+6){
更新Dmin
}
}
}
4,分析
无论将问题划分为两个规模减半的子问题,还是合并子问题的解,该算法都只需要线性时间。因此,假设n是2的幂,我们得到算法运行时间的递归式:
T(n) = 2T(n/2) + f(n)
其中f(n) ∈O(n).应用主定理,T(n) = O(nlogn).
5,源码
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
struct point {
int x = NULL;
int y = NULL;
};
void swap(int x, int y) {
int tmp;
tmp = x;
x = y;
y = tmp;
}
int direct(point P[], int n) {
int Dmin = 1000;
for (int i = 0; i < n - 1; i++) {
for (int j = i + 1; j < n; j++) {
int dist = (P[i].x - P[j].x) * (P[i].x - P[j].x) + (P[i].y - P[j].y) * (P[i].y - P[j].y);
if (Dmin > dist) Dmin = dist;
}
}
return Dmin;
}
int EfficientClosestPair(point Px[],point Py[], int n,int Dmin){
int mid = (n - 1) / 2 + 1;
if (n <= 3)//如果pointnum小于等于3,蛮力求解
{
Dmin = direct(Px, n);
return Dmin;
}
else
{
point Px1[(10 - 1) / 2 + 1];
point Py1[(10 - 1) / 2 + 1];
point Px2[10 / 2];
point Py2[10 / 2];
for (int i = 0; i < mid; i++) {
Px1[i].x = Px[i].x;
Px1[i].y = Px[i].y;
Px2[i].x = Px[i + 1].x;
Px2[i].y = Px[i + 1].y;
Py1[i].x = Py[i].x;
Py1[i].y = Py[i].y;
Py2[i].x = Py[i + 1].x;
Py2[i].y = Py[i + 1].y;
}
int Dmin1 = EfficientClosestPair(Px1, Py1,mid,Dmin);
int Dmin2 = EfficientClosestPair(Px2, Py2,n/2,Dmin);
Dmin = Dmin1 < Dmin2 ? Dmin1 : Dmin2;
point S[10];
int k = 0;
for (int i = 0; i < n; i++) {
if (Py[i].x >= mid - Dmin && Py[i].x <= mid + Dmin) {
S[k].x = Py[i].x;
S[k].y = Py[i].y;
k++;
}
}
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < i + 6; j++) {
int dist = (S[i].x - S[j].x) * (S[i].x - S[j].x) + (S[i].y - S[j].y) * (S[i].y - S[j].y);
if (Dmin > dist) Dmin = dist;
}
}
return Dmin;
}
}
int main() {
int n = 10;
point P[10];
int Dmin = 1000;
for (int i = 0; i < n; i++) {
scanf_s("%d%d", &P[i].x, &P[i].y);
}
point Px[10], Py[10];
for (int i = 0; i < n; i++) { //Px按照x轴从小到大排序
for (int j = 0; j < n - i - 1; j++) {
if (Px[j].x > Px[j + 1].x) {
swap(Px[j].x, Px[j + 1].x);
swap(Px[j].y, Px[j + 1].y);
}
}
}
for (int i = 0; i < n; i++) { //Py按照y轴从小到大排序
for (int j = 0; j < n - i - 1; j++) {
if (Py[j].y > Py[j + 1].y) {
swap(Py[j].x, Py[j + 1].x);
swap(Py[j].y, Py[j + 1].y);
}
}
}
printf("%d",EfficientClosestPair(Px, Py, n,Dmin));
return 0;
}