实验内容
编程实现求平面上n个顶点的最近点对问题
对文件data.txt的所有点,求距离最近的一对及其距离。
程序输入:
字符串文件data.txt。每行三个数,分别表示顶点编号和其横纵坐标
程序输出要求:
输出距离最近的一对顶点编号,及其距离值。
算法设计思路
这个程序是用来寻找二维平面上最近点对的距离以及这对点的编号。它采用了一种称为分治法的算法思想,这种方法可以将问题分解为较小的子问题,然后将这些子问题的解组合起来,形成原问题的解。
在这个程序中,首先读取了一个文件"data.txt",文件中包含了一系列点的坐标和编号。然后,将这些点按照x坐标和y坐标分别进行排序,这样可以方便后续的计算。
接着,采用了分治法的思想,将所有的点分为两个部分,分别计算左半部分和右半部分的最近点对的距离,然后取两者中的最小值。但是,这样可能会遗漏掉一种情况,即最近点对一部分在左半部分,一部分在右半部分。因此,还需要检查中间区域的点对,看是否有更近的点对存在。
在检查中间区域的点对时,采用一种优化的方法,即只检查y坐标在指定范围内的点对,因为如果两点的y坐标相差过大,那么它们的距离肯定会大于当前的最小距离。
在每一步中,都会更新当前的最小距离,以及对应的点对。最后,输出了最近点对的距离,以及这对点的编号。
源码及注释
#include<iostream>
#include<fstream>
#include<sstream>
#include<algorithm>
#include<cmath>
#include <vector>
#include <chrono>
std::vector<int> my_vector;
using namespace std;
struct node {
double x;
double y;
int id;//点的编号
};
vector<node> point;
int temp[100000];
int a = 2147483647, b = 2147483647;
double ans = 1e9;
double dis(node a, node b)
{
return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);
}
bool cmp_x(node a, node b)
{
return a.x <= b.x;
}//按照x坐标升序,排序以后才好分左右平面
bool cmp_y(node a, node b)
{
return a.y <= b.y;
}//按照y坐标升序
void Update(int new_a, int new_b, double new_ans)
{
if (new_a > new_b) swap(new_a, new_b);
if (new_ans < ans)
{
a = new_a;
b = new_b;
ans = new_ans;
}
}
void MiniDistance(int left, int right)
{
if (left == right) return;//如果自己和自己的距离最近,则距离为0,应该返回
if (left + 1 == right)
{
//更新一下答案
Update(point[left].id, point[right].id, dis(point[left], point[right]));
return;
}
//如果中间还有其他点,那就再中间分开。
int mid = (left + right) >> 1;
MiniDistance(mid, right);//找右边的最小距离
MiniDistance(left, mid - 1);//找左边的最小距离
int k = 0;
for (int i = left; i <= right; i++)
{
if (fabs(point[mid].x - point[i].x) <= ans)
{
temp[++k] = i;
}
}//有可能在两个平面中间还有其他的更小距离 这个就是按照x坐标找可能出现的点
//遍历,找这些点的最小距离
for (int i = 1; i < k; i++)
{
for (int j = i + 1; (j <= k)&&(j<i+8) ; j++)
{
if (ans >= dis(point[temp[i]], point[temp[j]]))
{
Update(point[temp[i]].id, point[temp[j]].id, dis(point[temp[i]], point[temp[j]]));
}
}
}
return;
}
int main(void)
{
ifstream infile("data.txt");
string line;
while (getline(infile, line))
{
istringstream iss(line);
int id;
double x, y;
iss >> id >> x >> y;
point.push_back({ x, y, id });
}
infile.close();
sort(point.begin() + 1, point.end(), cmp_x);
sort(point.begin() + 1, point.end(), cmp_y);
// 记录开始时间
auto start = chrono::high_resolution_clock::now();
// 调用最近点对算法
MiniDistance(1, point.size() - 1);
// 记录结束时间
auto end = chrono::high_resolution_clock::now();
// 计算程序运行时间(毫秒)
chrono::duration<double, milli> elapsed = end - start;
cout << "程序运行时间: " << elapsed.count() << " 毫秒" << endl;
// 输出最近点对的结果
cout << "最近点对的距离为: " << sqrt(ans) << endl;
cout << "最近点对的点为: " << a << " " << b << endl;
return 0;
}
实验结果分析与对比
时间复杂度分析
读取数据:该部分的时间复杂度为O(n),其中n是输入数据的数量。
排序:首先按照x坐标对点进行排序,然后按照y坐标对点进行排序。排序的时间复杂度为O(nlogn)。
分治算法:MiniDistance函数使用分治算法来找到最近点对。该算法将点集分为左右两个平面,并递归地在每个平面中寻找最近点对。时间复杂度可以表示为T(n) = 2T(n/2) + O(n),其中T(n)是处理n个点的时间复杂度。根据主定理,可以得到该算法的时间复杂度为O(nlogn)。
寻找中间可能的更小距离:在每次递归中,该算法会在两个平面中间寻找可能的更小距离。假设在两个平面中间的点的数量为m,寻找中间可能更小的距离的时间复杂度是O(m)。
综上所述,该程序的总时间复杂度为O(nlogn),其中n是输入数据的数量,m是两个平面中间的点的数量。