今天开始复习典型算法,首先回顾了分治算法的应用。一个经典的问题是求二维空间中最近点对。如果硬性用循环解决这个问题的话,当点的数量较大时,循环的次数会以O(n2)增加,所以这不是较好的解决方法。对于这种问题,可以通过将原问题不断划分为更小的子问题进而加以解决。在《数据结构、算法与应用-----c++语言描述》一书中给出了这一问题完备的分治递归解决方案。 算法过程如下:
1、如果点的数量n较小,则直接用暴力循环求解。
2、否则将点集分为大致相等的两个部分A和B。
3、分别确定A和B中距离最近的点对,并取较小者L。
4、求A与B中的最近点对。
5、取上面二者中较小的解为最终答案。
其中的关键问题在第4步,即如何求两个子问题之间的最近点对。可以将A和B中与分界线距离小于L的点找到,如果这些点中任意两点的纵向距离小于L则计算其距离,取最小距离即为所求。
按照书上的解释,代码如下:
// NearestPointsFinal.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
#include <math.h>
#include <vector>
#include <ctime>
#define squaresize 1000
using namespace std;
int count1=0;
class Npoint
{
public:
int ID;
int x;
int y;
};
void mysortX(vector<Npoint>& v)
{
const size_t s=v.size();
for(int gap=s/2;gap>0;gap/=2)
for(int i=gap;i<s;++i)
for(int j=i-gap;j>=0;j-=gap)
if(v[j+gap].x<v[j].x){
::count1++;
Npoint temp=v[j];
v[j]=v[j+gap];
v[j+gap]=temp;
}
}
void mysortY(vector<Npoint>& v)
{
const size_t s=v.size();
for(int gap=s/2;gap>0;gap/=2)
for(int i=gap;i<s;++i)
for(int j=i-gap;j>=0;j-=gap)
if(v[j+gap].y<v[j].y){
::count1++;
Npoint temp=v[j];
v[j]=v[j+gap];
v[j+gap]=temp;
}
}
template<typename T>
inline double dis(const T& p1,const T& p2)
{
double dx=p1.x-p2.x;
double dy=p1.y-p2.y;
return sqrt(dx*dx+dy*dy);
}
void Merge(vector<Npoint>& X,vector<Npoint>& Y,int l,int m,int r)
{
int i=l,j=m+1;
int k=l;
while((i<=m)&&(j<=r)){
::count1++;
if ((X[i].y)<(X[j].y)) { Y[k++]=X[i++];}
else Y[k++]=X[j++];
}
if (i>m) for (int q=j;q<=r;q++)
{
::count1++;
Y[k++]=X[q];
}
else for (int q=i;q<=m;q++)
{
::count1++;
Y[k++]=X[q];
}
};
void close(vector<Npoint>& X,vector<Npoint>& Y,vector<Npoint>& Z,
int l,int r,Npoint& p1,Npoint& p2, double& mindis)
{
::count1++;
if (0==(r-l))//如果只有一个点,则最近点对为其本身//
{
p1=X[0],p2=X[0];
mindis=sqrt(2.0*squaresize*squaresize);
return;
}
if (1==(r-l))//如果只有两个点,则最近点对为这两个点//
{
p1=X[0],p2=X[1];
mindis=dis(X[l],X[r]);
return ;
}
//////////////////////
double d1,d2,d3,mintemp;
if (2==(r-l))//如果有三个个点,则最近点对取较近的两个点//
{
d1=dis(X[l],X[l+1]);
d2=dis(X[l+1],X[l+2]);
d3=dis(X[l],X[l+2]);
mintemp=d1<d2?d1:d2;
mintemp=mintemp<d3?mintemp:d3;
if (mintemp==d1)
{ p1=X[l] , p2=X[l+1]; }
if (mintemp==d2)
{ p1=X[l+1] , p2=X[l+2]; }
if (mintemp==d3)
{ p1=X[l] , p2=X[l+2]; }
mindis=mintemp;
return ;
}
int m=(l+r)/2;
int f=l, g=m+1;
for (int i=l;i<=r;i++)
{
::count1++;
if (Y[i].ID>m)
{
Z[g++]=Y[i];
}
else Z[f++]=Y[i];
}
close(X,Z,Y,l,m,p1,p2,mindis);
double dis2;
Npoint p3,p4;
close(X,Z,Y,m+1,r,p3,p4,dis2);
if (dis2<mindis)
{
p1=p3,p2=p4,mindis=dis2;
}
Merge(Z,Y,l,m,r);
int k=l;
for (int i=l;i<=r;i++)
{
if (fabs((double)Y[m].x-(double)Y[i].x)<=mindis) Z[k++]=Y[i];
}
for (int i=l;i<k;i++)
for (int j=i+1;j<k&&fabs((double)Z[j].y-(double)Z[i].y)<=mindis;j++)
{
::count1++;
mintemp=dis(Z[i],Z[j]);
if (mintemp<mindis)
{
mindis=mintemp;
p1=X[Z[i].ID];
p2=X[Z[j].ID];
}
}
};
bool closest(vector<Npoint>& X,int n,Npoint& p1,Npoint& p2, double& mindis)
{
if (n<2)
{
mindis=sqrt(2.0*squaresize*squaresize);
return false;
}
mysortX(X);
vector<Npoint> Y;
vector<Npoint> Z;
int i=0 ;
for(vector<Npoint>::iterator it=X.begin() ; it!=X.end() ; it++,i++)
{
(*it).ID=i;
Y.push_back((*it));
Z.push_back((*it));
}
mysortY(Y);
close(X,Y,Z,0,n-1,p1,p2,mindis);
return true;
};
/*
pool: sum of points
low: start index of points
high: end index of points
p1&p2:the nearest points
*/
double NearestPermute(vector<Npoint>& pool,int low,int high,Npoint& p1,Npoint &p2)
{
if (0==(high-low))//如果只有一个点,则最近点对为其本身//
{
p1.x=pool[low].x;p1.y=pool[low].y;p2.x=pool[high].x;p2.y=pool[high].y;
return sqrt(2.0*squaresize*squaresize);
}
if (1==(high-low))//如果只有两个点,则最近点对为这两个点//
{
p1.x=pool[low].x;p1.y=pool[low].y;p2.x=pool[high].x;p2.y=pool[high].y;
return dis(pool[low],pool[high]);
}
double mintemp=sqrt(2.0*squaresize*squaresize);
double distemp=0.0;
for (int index1=low;index1<=high;index1++)
for (int index2=index1+1;index2<=high;index2++)
{
::count1++;
distemp=dis(pool[index1],pool[index2]);
if (distemp<mintemp)
{
mintemp=distemp;
p1.x=pool[index1].x,p1.y=pool[index1].y;
p2.x=pool[index2].x,p2.y=pool[index2].y;
}
}
return mintemp;
}
int _tmain(int argc, _TCHAR* argv[])
{
int sum=0;
cout<<"enter how many points"<<endl;
cin>>sum;
if (sum<=0)
return -1;
vector<Npoint> Pool;
Npoint temp;temp.x=0;temp.y=0;
Pool.reserve(sum);
for (int i=0;i<sum;i++)
Pool.push_back(temp);
//////////////////////////////////
srand(time(0));
for (int i=0;i<sum;i++)
Pool[i].x=(int)(rand()%squaresize);
for (int i=0;i<sum;i++)
Pool[i].y=(int)(rand()%squaresize);
//////////////////////////////////
cout<<"Random points are:"<<endl;
for (int i=0;i<sum;i++)
cout<<"("<<Pool[i].x<<","<<Pool[i].y<<")"<<" ";
cout<<endl;
/////////////////////////////////////////////
Npoint p1,p2;
double mindis;
closest(Pool,sum,p1,p2,mindis);
cout<<"Iteration:"<<::count1<<endl;
cout<<"min dis:"<<mindis<<endl;
cout<<"("<<p1.x<<","<<p1.y<<")"<<" ";
cout<<"("<<p2.x<<","<<p2.y<<")"<<" ";
cout<<endl;
/////////
::count1=0;
mindis=NearestPermute(Pool,0,sum-1,p1,p2);
cout<<"Iteration:"<<::count1<<endl;
cout<<"min dis:"<<mindis<<endl;
cout<<"("<<p1.x<<","<<p1.y<<")"<<" ";
cout<<"("<<p2.x<<","<<p2.y<<")"<<" ";
return 0;
}
运行结果如下图所示,上面是分治算法的迭代次数和结果,后者是暴力解法的迭代次数和结果(因为结果是求最小距离,所以距离最小情况下点对可能不同)。
但是偶尔会出现分治算法和暴击解法结果不一致的情况,还不清楚问什么,如下图所示。笔者猜想是A和B间最近点的计算有问题,还没有验证,希望有答案的同学和我联系啊。