分治法:最近点对问题,画图表示

C++ 最近点对问题(浮点数坐标及画图)

1. 讲解

在这里插入图片描述在这里插入图片描述
2. 算法分析(伪代码)
在这里插入图片描述
3. 程序代码
在看程序前,请务必看懂此题讲解和算法分析。相比较其他博主的程序,我的程序可能不是最优的,但我觉得是比较容易看懂的,因为本身我的技术就不太高。

#include<iostream>
#include <cstdlib>
#include <ctime>
#include<cmath>
#include <iomanip> 
#include <fstream>
#define random(a,b) (rand()%(b-a)+a)
#define N 30
using namespace std;
struct point				//点的横纵坐标(位置)用结构体来表示
{
	double x;
	double y;
};
struct distance_index		//每次划分都记录下来两个最近点对的距离和位置
{
	double distance;
	point index1;
	point index2;
};
int Partition(point r[], int first, int end, int flag)		//快排定轴函数
{
	int i = first, j = end;
	point temp;
	if (flag == 0)
	{
		while (i < j)
		{
			while (i < j&&r[i].x <= r[j].x) j--;
			if (i < j)
			{
				temp = r[i];
				r[i] = r[j];
				r[j] = temp;
				i++;
			}
			while (i < j&&r[i].x <= r[j].x) i++;
			if (i < j)
			{
				temp = r[i];
				r[i] = r[j];
				r[j] = temp;
				j--;
			}
		}
		return i;
	}
	else
	{
		while (i < j)
		{
			while (i < j&&r[i].y <= r[j].y) j--;
			if (i < j)
			{
				temp = r[i];
				r[i] = r[j];
				r[j] = temp;
				i++;
			}
			while (i < j&&r[i].y <= r[j].y) i++;
			if (i < j)
			{
				temp = r[i];
				r[i] = r[j];
				r[j] = temp;
				j--;
			}
		}
		return i;
	}
}
void Quicksort(point r[], int first, int end, int flag)		//快排
{
	int pivot;
	if (flag == 0)
	{
		if (first < end)
		{
			pivot = Partition(r, first, end, flag);
			Quicksort(r, first, pivot - 1, flag);
			Quicksort(r, pivot + 1, end, flag);

		}
	}
	else
	{
		if (first < end)
		{
			pivot = Partition(r, first, end, flag);
			Quicksort(r, first, pivot - 1, flag);
			Quicksort(r, pivot + 1, end, flag);

		}
	}
}
double Distance(point a, point b)
{
	return sqrt((a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y));
}
distance_index Closeset(point S[], int low, int high)
{
	int mid, i, j, index;
	point P[N];
	distance_index D1, D2, D3, D;
	if (high - low == 1)			//如果只有两个点则直接计算点对距离
	{
		D.distance = Distance(S[low], S[high]);
		D.index1 = S[low];
		D.index2 = S[high];
		return D;
	}			
	if (high - low == 2)			//如果只有三个点则直接计算点对距离
	{
		D1.distance = Distance(S[low], S[low+1]);
		D2.distance = Distance(S[low+1], S[high]);
		D3.distance = Distance(S[low], S[high]);
		if ((D1.distance < D2.distance) && (D1.distance < D3.distance))
		{
			D1.index1 = S[low];
			D1.index2 = S[low + 1];
			return D1;
		}
		else if (D2.distance < D3.distance)
		{
			D2.index1 = S[low+1];
			D2.index2 = S[high];
			return D2;
		}
		else
		{
			D3.index1 = S[low];
			D3.index2 = S[high];
			return D3;
		}
	}
	mid = (low + high) / 2;					//根据横坐标计算中间点(主函数里数组已按横坐标排好序了)
	D1 = Closeset(S, low, mid);				//递归求解子问题①
	D2 = Closeset(S, mid + 1, high);		//递归求解子问题②
	if (D1.distance <= D2.distance)
		D = D1;
	else 
		D = D2;
	index = 0;
	for (i = mid; (i >= low) && (S[mid].x - S[i].x < D.distance); i--)			//将中间点左边与中间点横坐标距离在d以内的点存到集合p中
		P[index++] = S[i];
	for (i = mid + 1; (i <= high) && (S[i].x - S[mid].x < D.distance); i++)		//将中间点右边与中间点横坐标距离在d以内的点存到集合p中
		P[index++] = S[i];
	Quicksort(P, 0, index - 1,1);		//对集合p中的点按y坐标升序排列,0代表横坐标排序,1代表纵坐标排序
	for (i = 0; i < index; i++)			//从小到大依次处理集合p中的点
	{
		for (j = i + 1; j < index; j++)
		{
			if (P[j].y - P[i].y >= D.distance)	//目的是找出与P[i]的纵坐标y在d范围内的点,所以一旦有一个点超出此范围,则其他点也会超出范围,因为已经升序排列
				break;
			else
			{
				D3.distance = Distance(P[i], P[j]);
				D3.index1 = P[i];
				D3.index2 = P[j];
				if (D3.distance < D.distance)
				{
					D = D3;
				}
			}
		}
	}
	return D;
}
int main()
{
	int i;
	point S[N];					//s为30个点对,t用来记录两个最近点对
	distance_index D;
	srand((int)time(0));		//保证每次运行时产生的随机数都不同
	for (i = 0; i < N; i++)									//取30个随机横坐标
		S[i].x = random(100, 1000)*0.01;					//随机取一个[1,10)的浮点数(保留两位小数)
	for (i = 0; i < N; i++)									//取30个随机纵坐标
		S[i].y = random(100, 1000)*0.01;					//随机取一个[1,10)的浮点数(保留两位小数)
	Quicksort(S, 0, N - 1, 0);			//对集合s中的点按横坐标升序排列,0代表横坐标排序,1代表纵坐标排序
	cout << "按横坐标升序排列,30个坐标点分别为:" << endl;
	for (i = 0; i < N; i++)
		cout << "(" << S[i].x << "," << S[i].y << ")" << endl;
	D = Closeset(S, 0, N-1);
	cout << "两个最近点对分别为" << "(" << D.index1.x << "," << D.index1.y << ")和(" << D.index2.x << "," << D.index2.y << ")" << endl;
	cout <<"最近点对距离为:"<< D.distance<<endl;
	ofstream fout("E:\\编程\\算法实验一\\index.txt");		//将所有坐标写进文件
	if (!fout)
		cout << "文件不能打开" << endl;
	else
	{
		for (i = 0; i < N; i++)
			fout << S[i].x << ' ' << S[i].y << endl;
		fout << D.index1.x << ' ' << D.index1.y << endl;		//最近点对记录在文件最后两行
		fout << D.index2.x << ' ' << D.index2.y << endl;
		cout << "文件能打开" << endl;
	}
	getchar();
	getchar();
	return 0;
}

cout <<“最近点对距离为:”<< D.distance<<endl;

其实到这行代码(185行),算法程序已经执行完毕,最近点对和最近距离都已经输出,后面的几行代码是为画图做准备

ofstream fout(“E:\编程\算法实验一\index.txt”); //将所有坐标写进文件
if (!fout)
cout << “文件不能打开” << endl;
else
{
for (i = 0; i < N; i++)
fout << S[i].x << ’ ’ << S[i].y << endl;
fout << D.index1.x << ’ ’ << D.index1.y << endl; //最近点对记录在文件最后两行
fout << D.index2.x << ’ ’ << D.index2.y << endl;
cout << “文件能打开” << endl;
}

这几行代码将产生的随机坐标及最终计算得出的最近点对写入txt文件,然后利用Python去读取这个文件里的坐标,并画出来,下面是绘图代码,用到的软件是Anaconda里的Spyder。

# -*- coding: utf-8 -*-
"""
Created on Wed Apr 15 13:48:58 2020

@author: lenovo
"""
import matplotlib.pyplot as plt
import numpy as np
file_path='E:\\编程\\算法实验一\\index.txt'
X,Y = [],[]
with open(file_path, 'r') as f:
    lines = f.readlines()
    for line in lines:
        value = [float(s) for s in line.split()]
        X.append(value[0])
        Y.append(value[1])
print(X)
print(Y)        
for i in range(len(X)):
    plt.scatter(X[i],Y[i],c='BLUE',alpha=0.5,s=50)
    
plt.scatter(X[-1],Y[-1],c='RED',alpha=1,s=50)
plt.scatter(X[-2],Y[-2],c='RED',alpha=1,s=50)
print(X[-1])
x=[[X[-1],X[-2]]]
y=[[Y[-1],Y[-2]]]
plt.plot(x[0],y[0],c='GREEN')
plt.xticks(np.arange(0,11,1))
plt.yticks(np.arange(0,11,1))
plt.show()

5.程序运行截图
在这里插入图片描述在这里插入图片描述
6. 总结
算法部分的注释我觉得足够看懂了,如果看不懂可能就是对这道题及分治法没理解够,再看看书和老师课件吧。
程序中,如果没有distance_index这个结构体来时刻记录坐标,可能只能输出最近点对距离,却无法输出两个最近点对坐标(我能想到的方法就是利用结构体),而无法输出两个最近点对坐标就无法画图连线,这是distance_index这个结构体的作用。
另外,快排过程中,Point这个结构体是无法排序的,要对结构体里的横纵坐标排序,所以快排及定轴函数的最后一个参数flag就起到选择x或y坐标排序的功能。
此程序只是为了计算结果,在时间复杂度上可能不太好,望各位多多指教。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值