1. 问题
最近对问题要求在包含有n个点的集合S中,找出距离最近的两个点。设 p1(x1,y1),p2(x2,y2),……,pn(xn,yn)是平面的n个点。严格地讲,最近点对可能不止一对,此例输出一对即可。
2. 解析
在蛮力法实现最近点对问题中,将问题简化:距离最近的点对可能多于一对,找出一对即可,另外只考虑二维平面中的情况。此处考虑到
直接用公式计算其距离
在利用分治法思想解决此问题时,首先考虑将最近对问题进行分治,设计其分治策略。将集合S分成两个子集S1和S2,根据
平衡子问题原则,每个子集中的点数大致都为n/2。这样分治后,最近点对将会出现三种情况:在S1中,在S2中或者最近
点对分别在集合S1和S2中。利用递归分析法分别计算前两种情况,第三种方法另外分析。求解出三类子情况后,
再合并三类情况,比较分析后输出三者中最小的距离。
3. 设计
#include<stdlib.h>
#include<stdio.h>
#include<iostream>
#include<math.h>
#define MAX 1000000
using namespace std;
double closestPoints(double x[], double y[], int n) {
double x1, x2, y1, y2;
double dist, minDist = MAX;
for (int i = 0; i < n; i++)
for (int j = i + 1; j < n; j++) {
dist = (x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j]);
if (dist < minDist) {
minDist = dist;
x1 = x[i]; y1 = y[i];
x2 = x[j]; y2 = y[j];
}
}
cout << "最近点对为:(" << x1 << "," << y1 << ")-(" << x2 << "," << y2 << ")";
return minDist;
}
int main() {
double x[100], y[100];
double minDist;
int n;
cout << "输入点的个数:\n";
cin >> n;
cout << "输入点集的坐标:\n";
for (int i = 0; i < n; i++)
cin >> x[i] >> y[i];
minDist = closestPoints(x, y, n);
cout << "其距离为:\n" << sqrt(minDist);
return 0;
}
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<algorithm>
#include<iostream>
using namespace std;
struct point {
double x, y;
};
double Distance(point a, point b) {
return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
bool cmp(point a, point b) {
return a.y < b.y;
}
bool cmp2(point a, point b) {
return a.x < b.x;
}
double closestPoint(point s[], int low, int high, point rec[]) {
double d1, d2, d3, d;
int mid, i, j, index;
double x1, y1, x2, y2;
point P[high - low + 1], temp1[2], temp2[2], temp3[2];
if (high - low == 1) {
rec[0].x = s[low].x; rec[0].y = s[low].y;
rec[1].x = s[high].x; rec[1].y = s[high].y;
return Distance(s[low], s[high]);
}
if (high - low == 2) {
d1 = Distance(s[low], s[low + 1]);
d2 = Distance(s[low + 1], s[high]);
d3 = Distance(s[low], s[high]);
if ((d1 < d2) && (d1 < d3)) {
rec[0].x = s[low].x; rec[0].y = s[low].y;
rec[1].x = s[low + 1].x; rec[1].y = s[low + 1].y;
return d1;
}
else if (d2 < d3) {
rec[0].x = s[low + 1].x; rec[0].y = s[low + 1].y;
rec[1].x = s[high].x; rec[1].y = s[high].y;
return d2;
}
else {
rec[0].x = s[low].x; rec[0].y = s[low].y;
rec[1].x = s[high].x; rec[1].y = s[high].y;
return d3;
}
}
mid = (low + high) / 2;
d1 = closestPoint(s, low, mid, rec);
temp1[0] = rec[0];
temp1[1] = rec[1];
d2 = closestPoint(s, mid + 1, high, rec);
temp2[0] = rec[0];
temp2[1] = rec[1];
if (d1 < d2) {
d = d1;
rec[0] = temp1[0];
rec[1] = temp1[1];
}
else {
d = d2;
rec[0] = temp2[0];
rec[1] = temp2[1];
}
index = 0;
for (i = mid; (i >= low) && ((s[mid].x - s[i].x) < d); i--)
P[index++] = s[i];
for (i = mid + 1; (i <= high) && ((s[i].x - s[mid].x) < d); i++)
P[index++] = s[i];
sort(P, P + index, cmp);
for (i = 0; i < index; i++) {
for (j = j + 1; j < index; i++) {
if ((P[j].y - P[i].y) >= d)
break;
else {
d3 = Distance(P[i], P[j]);
if (d3 < d) {
rec[0].x = P[i].x; rec[0].y = P[i].y;
rec[1].x = P[j].x; rec[1].y = P[j].y;
d = d3;
}
}
}
}
return d;
}
int main() {
point p[10];
int n;
double minDist;
cout << "输入点的个数:\n";
cin >> n;
cout << "输入点集:(x,y)\n";
for (int i = 0; i < n; i++)
cin >> p[i].x >> p[i].y;
sort(p, p + n, cmp2);
point index[2];
minDist = closestPoint(p, 0, n - 1, index);
cout << "最小距离点对为:(" << index[0].x << "," << index[0].y << "),(" << index[1].x << "," << index[1].y << ")\n";
cout << "最小距离为:\n" << minDist;
return 0;
}
4. 分析
通过遍历所有点集,计算出每一个点对的距离,计算出最近的距离并输出。避免同一对点计算两次,只考虑i<j的点对(pi,pj)。
其主要循环的步骤就是求出平方值,执行的次数为
在分治算法中,当求解n个点的集合的最近点对时,对于上述三类情况中的前两者可由递归算得,
而分析可得第三类情况的时间代价 ,合并后问题求解的总的时间按复杂度可由下列公式:
5. 源码
https://github.com/Alt0191/—/blob/main/%E5%88%86%E6%B2%BB%E6%B3%95
https://github.com/Alt0191/—/blob/main/%E6%9A%B4%E5%8A%9B%E6%B3%95