最近点对问题定义:已知上m个点的集合,找出对接近的一对点。
在二维空间里,可用分治法求解最近点对问题。预处理:分别根据点的x轴和y轴坐标进行排序,得到X和Y,很显然此时X和Y中的点就是S中的点。
情况(1):点数小于等于三时:
情况(2):点数大于三时:
首先划分集合S为SL和SR,使得SL中的每一个点位于SR中每一个点的左边,并且SL和SR中点数相同。分别在SL和SR中解决最近点对问题,得到DL和DR,分别表示SL和SR中的最近点对的距离。令d=min(DL,DR)。如果S中的最近点对(P1,P2)。P1、P2两点一个在SL和一个在SR中,那么P1和P2一定在以L为中心的间隙内,以L-d和L+d为界,如下图所示:
如果在SL中的点P和在SR中的点Q成为最近点对,那么P和Q的距离必定小于d。因此对间隙中的每一个点,在合并步骤中,只需要检验yp+d和yp-d内的点即可。
步骤1:根据点的y值和x值对S中的点排序。
步骤2:找出中线L将S划分为SL和SR
步骤3:将步骤2递归的应用解决SL和SR的最近点对问题,并令d=min(dL,dR)。
步骤4:将L-d~L+d内的点以y值排序,对于每一个点(x1,y1)找出y值在y1-d~y1+d内的所有点,计算距离为d'。 如果d'小于d,令d=d',最后的d值就是答案。
实验1 递归与分治算法
一,实验目的和要求
(1)进一步掌握递归算法的设计思想以及递归程序的调试技术;
(2)理解这样一个观点:分治与递归经常同时应用在算法设计之中。
(3)分别用蛮力法和分治法求解最近对问题;
(4)分析算法的时间性能,设计实验程序验证分析结论。
二,实验内容
设p1=(x1, y1), p2=(x2, y2), …, pn=(xn, yn)是平面上n个点构成的集合S,设计算法找出集合S中距离最近的点对。
三,实验环境
Turbo C 或VC++
四,实验学时
2学时,必做实验
五,数据结构与算法
#include<iostream.h>
#include<cmath>
#define TRUE 1
#define FALSE 0
typedef struct Node
{
double x;
double y;
}Node; //坐标
typedef struct List
{
Node* data; //点
int count; //点的个数
}List;
typedef struct CloseNode
{
Node a;
Node b; //计算距离的两个点
double space; //距离平方
}CloseNode;
int n; //点的数目
//输入各点到List中
void create(List &L)
{
cout<<"请输入平面上点的数目:\n";
cin>>n;
L.count=n;
L.data = new Node[L.count]; //动态空间分配
cout<<"输入各点坐标 :x_y):"<<endl;
for(int i=0;i<L.count;++i)
cin>>L.data[i].x>>L.data[i].y;
}
//求距离的平方
double square(Node a,Node b)
{
return ((a.x-b.x)*(a.x-b.x))+((a.y-b.y)*(a.y-b.y));
}
//蛮力法
void BruteForce(const List &L,CloseNode &cnode,int begin,int end)
{
for(int i=begin;i<=end;++i)
{
for(int j=i+1;j<=end;++j)
{
double space=square(L.data[i],L.data[j]);
if(space<cnode.space)
{
cnode.a=L.data[i];
cnode.b=L.data[j];
cnode.space=space;
}
}
}
}
//冒泡排序
void BubbleSort(Node r[],int length)
{
int change,n;
n=length;change=TRUE;
double b,c;
for(int i=0;i<n-1&&change;++i)
{
change=FALSE;
for(int j=0;j<n-i-1;++j)
{
if(r[j].x>r[j+1].x)
{
b=r[j].x;c=r[j].y;
r[j].x=r[j+1].x;r[j].y=r[j+1].y;
r[j+1].x=b;r[j+1].y=c;
change=TRUE;
}
}
}
}
//分治法中先将坐标按X轴从小到大的顺序排列
void paixu(List L)
{
BubbleSort(L.data,L.count); //调用冒泡排序
}
//左右各距中线d的区域的最近对算法
void middle(const List & L,CloseNode &cnode,int mid,double midX)
{
int i,j; //分别表示中线左边,右边的点
double d=sqrt(cnode.space);
i=mid;
while(i>=0&&L.data[i].x>=(midX-d)) //在左边的d区域内
{
j=mid;
while(L.data[++j].x<=(midX+d)&&j<=L.count) //在右边的d区域内
{
if(L.data[j].y<(L.data[i].y-d)||L.data[j].y>(L.data[i].y+d)) //判断纵坐标是否在左边某固定点的2d区域内
continue;
double space = square(L.data[i],L.data[j]);
if(cnode.space>space) //在满足条件的区域内依次判断
{
cnode.a=L.data[i];
cnode.b=L.data[j];
cnode.space=space;
}
}
--i;
}
}
//分治法求最近对
void DivideConquer(const List &L,CloseNode &closenode,int begin,int end)
{
if(begin!=end)
{
int mid = (begin+end)/2; //排列后的中间的那个点
double midX = L.data[mid].x;
DivideConquer(L,closenode,begin,mid); //继续在左半边用分治法求最近对
DivideConquer(L,closenode,mid+1,end); //继续在右半边用分治法求最近对
middle(L,closenode,mid,midX); //判断左右各距中线d的区域,是否有最近对
}
}
void main()
{
//初始化
List list;
CloseNode closenode;
closenode.square=10000;//必须付初值,根据实际情况而定
closenode.space = 10000; //最近点的距离
create(list); //输入各点到NList中
cout<<"各点坐标为:"<<endl;
for(int i=0;i<list.count;++i)
cout<<"X="<<list.data[i].x<<" Y="<<list.data[i].y<<"\n";
BruteForce(list,closenode,0,list.count-1);
cout<<"用蛮力法求最近对:"<<endl;
cout<<"最近对为点 ("<<closenode.a.x<<","<<closenode.a.y<<")和点("<<closenode.b.x<<","<<closenode.b.y<<")\n"<<"最近距离为: "<<sqrt(closenode.space)<<endl;
cout<<endl<<endl;
cout<<"用分治法求最近对:"<<endl;
paixu(list);
cout<<"经过排序后的各点:"<<endl;
for(int j=0;j<list.count;++j)
cout<<"X="<<list.data[j].x<<" Y="<<list.data[j].y<<"\n";
DivideConquer(list,closenode,0,list.count-1);
cout<<"最近对为点 ("<<closenode.a.x<<","<<closenode.a.y<<")和点("<<closenode.b.x<<","<<closenode.b.y<<")\n"<<"最近距离为: "<<sqrt(closenode.space)<<endl;
}
实验1 递归与分治算法
一,实验目的和要求
(1)进一步掌握递归算法的设计思想以及递归程序的调试技术;
(2)理解这样一个观点:分治与递归经常同时应用在算法设计之中。
(3)分别用蛮力法和分治法求解最近对问题;
(4)分析算法的时间性能,设计实验程序验证分析结论。
二,实验内容
设p1=(x1, y1), p2=(x2, y2), …, pn=(xn, yn)是平面上n个点构成的集合S,设计算法找出集合S中距离最近的点对。
三,实验环境
Turbo C 或VC++
四,实验学时
2学时,必做实验
五,数据结构与算法
#include<iostream.h>
#include<cmath>
#define TRUE 1
#define FALSE 0
typedef struct Node
{
double x;
double y;
}Node; //坐标
typedef struct List
{
Node* data; //点
int count; //点的个数
}List;
typedef struct CloseNode
{
Node a;
Node b; //计算距离的两个点
double space; //距离平方
}CloseNode;
int n; //点的数目
//输入各点到List中
void create(List &L)
{
cout<<"请输入平面上点的数目:\n";
cin>>n;
L.count=n;
L.data = new Node[L.count]; //动态空间分配
cout<<"输入各点坐标 :x_y):"<<endl;
for(int i=0;i<L.count;++i)
cin>>L.data[i].x>>L.data[i].y;
}
//求距离的平方
double square(Node a,Node b)
{
return ((a.x-b.x)*(a.x-b.x))+((a.y-b.y)*(a.y-b.y));
}
//蛮力法
void BruteForce(const List &L,CloseNode &cnode,int begin,int end)
{
for(int i=begin;i<=end;++i)
{
for(int j=i+1;j<=end;++j)
{
double space=square(L.data[i],L.data[j]);
if(space<cnode.space)
{
cnode.a=L.data[i];
cnode.b=L.data[j];
cnode.space=space;
}
}
}
}
//冒泡排序
void BubbleSort(Node r[],int length)
{
int change,n;
n=length;change=TRUE;
double b,c;
for(int i=0;i<n-1&&change;++i)
{
change=FALSE;
for(int j=0;j<n-i-1;++j)
{
if(r[j].x>r[j+1].x)
{
b=r[j].x;c=r[j].y;
r[j].x=r[j+1].x;r[j].y=r[j+1].y;
r[j+1].x=b;r[j+1].y=c;
change=TRUE;
}
}
}
}
//分治法中先将坐标按X轴从小到大的顺序排列
void paixu(List L)
{
BubbleSort(L.data,L.count); //调用冒泡排序
}
//左右各距中线d的区域的最近对算法
void middle(const List & L,CloseNode &cnode,int mid,double midX)
{
int i,j; //分别表示中线左边,右边的点
double d=sqrt(cnode.space);
i=mid;
while(i>=0&&L.data[i].x>=(midX-d)) //在左边的d区域内
{
j=mid;
while(L.data[++j].x<=(midX+d)&&j<=L.count) //在右边的d区域内
{
if(L.data[j].y<(L.data[i].y-d)||L.data[j].y>(L.data[i].y+d)) //判断纵坐标是否在左边某固定点的2d区域内
continue;
double space = square(L.data[i],L.data[j]);
if(cnode.space>space) //在满足条件的区域内依次判断
{
cnode.a=L.data[i];
cnode.b=L.data[j];
cnode.space=space;
}
}
--i;
}
}
//分治法求最近对
void DivideConquer(const List &L,CloseNode &closenode,int begin,int end)
{
if(begin!=end)
{
int mid = (begin+end)/2; //排列后的中间的那个点
double midX = L.data[mid].x;
DivideConquer(L,closenode,begin,mid); //继续在左半边用分治法求最近对
DivideConquer(L,closenode,mid+1,end); //继续在右半边用分治法求最近对
middle(L,closenode,mid,midX); //判断左右各距中线d的区域,是否有最近对
}
}
void main()
{
//初始化
List list;
CloseNode closenode;
closenode.square=10000;//必须付初值,根据实际情况而定
closenode.space = 10000; //最近点的距离
create(list); //输入各点到NList中
cout<<"各点坐标为:"<<endl;
for(int i=0;i<list.count;++i)
cout<<"X="<<list.data[i].x<<" Y="<<list.data[i].y<<"\n";
BruteForce(list,closenode,0,list.count-1);
cout<<"用蛮力法求最近对:"<<endl;
cout<<"最近对为点 ("<<closenode.a.x<<","<<closenode.a.y<<")和点("<<closenode.b.x<<","<<closenode.b.y<<")\n"<<"最近距离为: "<<sqrt(closenode.space)<<endl;
cout<<endl<<endl;
cout<<"用分治法求最近对:"<<endl;
paixu(list);
cout<<"经过排序后的各点:"<<endl;
for(int j=0;j<list.count;++j)
cout<<"X="<<list.data[j].x<<" Y="<<list.data[j].y<<"\n";
DivideConquer(list,closenode,0,list.count-1);
cout<<"最近对为点 ("<<closenode.a.x<<","<<closenode.a.y<<")和点("<<closenode.b.x<<","<<closenode.b.y<<")\n"<<"最近距离为: "<<sqrt(closenode.space)<<endl;
}