求平面上n个顶点的最近点对问题

实验内容

编程实现求平面上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是两个平面中间的点的数量。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值