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坐标排序的功能。
此程序只是为了计算结果,在时间复杂度上可能不太好,望各位多多指教。