中科大算法实验4求平面最近点对算法Word格式文档下载.docx
《中科大算法实验4求平面最近点对算法Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《中科大算法实验4求平面最近点对算法Word格式文档下载.docx(13页珍藏版)》请在冰豆网上搜索。
如果组成S的最接近点对的2个点都在S1中或都在S2中,则问题很容易解决。
但是,如果这2个点分别在S1和S2中,则对于S1中任一点p,S2中最多只有n/2个点与它构成最接近点对的候选者,仍需做n2/4次计算和比较才能确定S的最接近点对。
因此,依此思路,合并步骤耗时为O(n2)。
整个算法所需计算时间T(n)应满足:
T(n)=2T(n/2)+O(n2)
在一维的情形,距分割点距离为δ的2个区间(m-δ,m](m,m+δ]中最多各有S中一个点。
因而这2点成为唯一的末检查过的最接近点对候选者。
二维的情形则要复杂些,此时,P1中所有点与P2中所有点构成的点对均为最接近点对的候选者。
在最坏情况下有n2/4对这样的候选者。
但是P1和P2中的点具有以下的稀疏性质,它使我们不必检查所有这n2/4对候选者。
考虑P1中任意一点p,它若与P2中的点q构成最接近点对的候选者,则必有d(p,q)<
δ。
满足这个条件的P2中的点有多少个呢?
容易看出这样的点一定落在一个δ×
2δ的矩形R中.
因此d(u,v)≤5δ/6<
δ。
这与δ的意义相矛盾。
也就是说矩形R中最多只有6个S中的点。
图4(b)是矩形R中含有S中的6个点的极端情形。
由于这种稀疏性质,对于P1中任一点p,P2中最多只有6个点与它构成最接近点对的候选者。
因此,在分治法的合并步骤中,我们最多只需要检查6×
n/2=3n对候选者,而不是n2/4对候选者。
这是否就意味着我们可以在O(n)时间内完成分治法的合并步骤呢?
现在还不能作出这个结论,因为我们只知道对于P1中每个S1中的点p最多只需要检查P2中的6个点(确切的说,是矩形R中的6个S中的点),但是我们并不确切地知道要如何检查。
为了解决这个问题,我们可以将p和P2中所有S2的点投影到垂直线l上。
由于能与p点一起构成最接近点对候选者的S2中点一定在矩形R中,并且要满足d(p,q)<
δq∈P2,因此满足条件的点它们在直线l上的投影点距p在l上投影点的距离小于δ,由上面的分析可知,这种投影点不多于6个。
因此,若将P1和P2中所有S的点按其y坐标排好序,则对P1中所有点p,对排好序的点列作一次扫描,就可以找出所有最接近点对的候选者,对P1中每一点最多只要检查P2中排好序的相继6个点。
至此,我们可以给出用分治法求二维最接近点对的算法CPAIR2如下:
functionCPAIR2(S);
begin
if|S|=2
thenδ:
=S中这2点的距离
elseif|S|=0
=∞
else
1.m:
=S中各点x坐标值的中位数;
构造S1和S2,使S1={p∈S|px≤m}和S2={p∈S|px>
m}
2.δ1:
=CPAIR2(S1);
δ2:
=CPAIR2(S2);
3.δm:
=min(δ1,δ2);
4.设P1是S1中距垂直分割线l的距离在δm之内的所有点组成的集合,P2是S2中距分割线l的距离在δm之内所有点组成的集合。
将P1和P2中的点依其y坐标值从小到大排序,并设P1*和P2*是相应的已排好序的点列;
5.通过扫描P1*以及对于P1*中每个点检查P2*中与其距离在δm之内的所有点(最多6个)可以完成合并。
当P1*中的扫描指针逐次向上移动时,P2*中的扫描指针可在宽为2δm的一个区间内移动。
设δl是按这种扫描方式找到的点对间的最小距离;
6.δ=min(δm,δl);
end;
return(δ);
下面我们来分析一下算法CPAIR2的计算复杂性。
设对于n个点的平面点集S,算法耗时T(n)。
算法的第1步和第5步用了O(n)时间,第3步和第6步用了常数时间,第2步用了2T(n/2)时间。
若在每次执行第4步时进行排序,则在最坏情况下第4步要用O(nlogn)时间。
这不符合我们的要求。
因此,在这里我们要作一个技术上的处理。
我们采用设计算法时常用的预排序技术,即在使用分治法之前,预先将S中n个点依其y坐标值排好序,设排好序的点列为P*。
在执行分治法的第4步时,只要对P*作一次线性扫描,即可抽取出我们所需要的排好序的点列P1*和P2*。
然后,在第5步中再对P1*作一次线性扫描,即可求得δl。
因此,第4步和第5步的两遍扫描合在一起只要用O(n)时间。
这样一来,经过预排序处理后的算法CPAIR2所需的计算时间T(n)满足递归方程:
显而易见T(n)=O(nlogn),预排序所需的计算时间为O(n1ogn)。
因此,整个算法所需的计算时间为O(nlogn)。
在渐近的意义下,此算法已是最优的了。
二、核心代码:
1.生成随机数:
如图,使用C#代码,先生成坐标的随机数:
2.生成的随机数之后,将这些点以中位数分治:
Closetpair是递归函数。
3.求中位数的最进点对,将这些点对放到新的数组中:
4.将这些最近点对按y坐标排序:
由于C#库函数只能对数组排序,不得不将对象数组取出来,拍完序之后再放回去:
5.每个点与附近7个点进行比较:
三、结果与分析:
1.输出15,随机生成15个坐标对:
2.输入155,随机生成坐标对:
随机生成15万个:
分析:
如果在递归调用时,不对Y排序,非常卡,运行15万个坐标至少要运行5分钟。
。
所以,主函数要对X排序,递归时要对Y排序。
四、备注
总结:
本实验重点:
(1)主函数对X排序,递归时对Y排序。
(2)筛选的中位数附近的点,对每个点取六个比较即可。
(3)如图:
附录(源代码)
算法源代码(C/C++/JAVA描述)C#:
publicpartialclassForm1:
Form
{
publicForm1()
InitializeComponent();
}
publicclassPoint
publicfloatx,y;
publicstaticfloatNo_distance=1000000;
publicstaticfloatAAA,BBB,CCC,DDD;
publicstaticvoidSetPoints(Point[]points,intlength)
Randomra=newRandom();
for(inti=0;
i<
length;
i++)
points[i]=newPoint();
points[i].x=(float)ra.NextDouble()*200-100;
points[i].y=(float)ra.NextDouble()*200-100;
publicstaticfloatDistance(Pointa,Pointb)
return(float)Math.Sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
publicstaticfloatClosetspair(Point[]points,intlength,Pointa,Pointb)
floatdistance;
//记录集合中最近的两点
floatd1,d2;
//记录分割后两个子集中各自最小点对距离
inti=0,j=0,k=0;
//用于控制for循环的循环变量
Pointa1=newPoint();
Pointb1=newPoint();
Pointa2=newPoint();
Pointb2=newPoint();
//保存分割后两个子集中最小点对
if(length<
2)returnNo_distance;
//如果子集长度小于2,定义为最大距离,表示不可达
if(length==2)
distance=Distance(points[0],points[1]);
else
Point[]pts1=newPoint[length];
//两个子集分别放入这两个
Point[]pts2=newPoint[length];
floatmid=points[(length-1)/2].x;
//排完序后的中间下标值,即中位数
for(i=0;
length/2;
pts1[i]=points[i];
for(j=0,i=length/2;
pts2[j++]=points[i];
d1=Closetspair(pts1,length/2,a1,b1);
d2=Closetspair(pts2,length-length/2,a2,b2);
if(d1<
d2){distance=d1;
a=a1;
b=b1;
else{distance=d2;
a=a2;
b=b2;
//求解跨分割线,并在6*26区间内最近的点对
Point[]pts3=newPoint[length];
for(i=0,k=0;
if(Math.Abs(points[i].x-mid)<
=distance)pts3[k++]=points[i];
float[]array=newfloat[k];
for(intiii=0;
iii<
k;
iii++)
array[iii]=pts3[iii].x;
Array.Sort(array);
for(intia=0;
ia<
ia++)
pts3[ia].x=array[ia];
for(j=i+1;
j<
=i+7&
&
j++)//只需与有序的领接的的7个点进行比较
if(Distance(pts3[i],pts3[j])<
distance)
{//如果跨分割线的两点距离小于已知最小距离,则记录该距离
distance=Distance(pts3[i],pts3[j]);
a=pts3[i];
b=pts3[j];
AAA=a.x;
BBB=a.y;
CCC=b.x;
DDD=b.y;
returndistance;
privatevoidbutton1_Click(objectsender,EventArgse)
intN;
//随机生成的点对个数。
Pointa=newPoint();
Pointb=newPoint();
floatdistence;
if(textBox2.Text=="
"
)
MessageBox.Show("
请您先输入需要随机生成的多少对?
"
你好!
输入为空"
MessageBoxButtons.OKCancel);
else{
N=Int32.Parse(textBox2.Text.Trim());
if(N<
2)
请输入大于等于2的点个数!
!
);
Point[]points=newPoint[N];
SetPoints(points,N);
N;
listBox1.Items.Add("
("
+points[i].x+"
+points[i].y+"
)"
float[]array=newfloat[N];
array[iii]=points[iii].x;
points[ia].x=array[ia];
listBox2.Items.Add("
distence=Closetspair(points,N,a,b);
textBox1.Text="
最近点对为:
+"
+AAA+"
+BBB+"
)和"
+CCC+"
+DDD+"
;
textBox3.Text="
最近点对距离为:
+distence;
}