当前位置:   article > 正文

【深圳大学算法设计与分析】实验二 分治法求最近点对问题_深圳大学算法设计与分析实验二

深圳大学算法设计与分析实验二

实验目的

  1. 掌握分治法思想。
  2. 学会最近点对问题求解方法。

实验内容与结果

实验要求

1. 对于平面上给定的N个点,给出所有点对的最短距离,即,输入是平面上的N个点,输出是N点中具有最短距离的两点。

2. 要求随机生成N个点的平面坐标,应用蛮力法编程计算出所有点对的最短距离。

3. 要求随机生成N个点的平面坐标,应用分治法编程计算出所有点对的最短距离。

4. 分别对N=100000—1000000,统计算法运行时间,比较理论效率与实测效率的差异,同时对蛮力法和分治法的算法效率进行分析和比较。

5. 如果能将算法执行过程利用图形界面输出,可获加分。

分治法思想:将一个难以直接解决的大问题,分割成规模较小的相同问题。

具体的说:

1.将待求解的较大规模的问题分割为k个小规模的问题。

2.对这k个子问题分别求解。

3.如果子问题的规模仍然不够小,接着分割,直到问题规模小到可以直接求出解为止。

最近点对问题:对平面上给定的n个点,从所有点对中找出最短距离,即,输入是平面上的n个点,输出是n点中具有最短距离的两点。

1.暴力法

n个点存在n(n-1)/2个点对,逐一查阅各个点对,找出最小的值。

伪代码如下:

时间复杂度:
因为需要遍历n(n-1)/2种情况来找出最小值,最好、最坏、平均情况的时间复杂度都相同,为O(n) = T(n(n-1)/2) = O(n2)

空间复杂度:
因为需要一个变量来存储最小值,所以空间复杂度为O(1)。


运行时间

可以看出,理论时间和实际时间基本吻合,实际时间略小于理论时间;随着数据量的增加,运行时间急剧上升。

2.分治法

1.  预处理:根据输入点集S中的x轴和y轴坐标进行排序,得到X和Y,很显然此时X和Y中的点就是S中的点。

2.  点数较少时的情形


当点数较少时,可以直接进行计算。


3.  点数|S|>3时,将平面点集S分割成为大小大致相等的两个子集SL和SR,选取一个垂直线L作为分割直线,如何以最快的方法尽可能均匀平分?注意这个操作如果达到θ(n^2)效率,将导致整个算法效率达到θ(n^2)。

当点数较多时,应采取分治法,将平面分割,此时存在三种情况:
[1] 最短点对p1,p2均在左侧。
[2] 最短点对p1在左侧,p2在右侧
[3] 最短点对p1,p2均在右侧。

4.  两个递归调用,分别求出SL和SR中的最短距离为dl和dr。
对于情况[1]和情况[3],运用递归可以简便的解决,难点在于情况[2]。

5.  取d=min(dl, dr),在直线L两边分别扩展d,得到边界区域Y,Y’是区域Y中的点按照y坐标值排序后得到的点集(为什么要排序?),Y'又可分为左右两个集合Y’L和Y’R

对于中间的部分,按y进行排序,方便后面进行点的比较。

6.  对于Y’L中的每一点,检查Y’R中的点与它的距离,更新所获得的最近距离,注意这个步骤的算法效率,请务必做到线性效率,并在实验报告中详细解释为什么能做到线性效率?

由中线左边和中线右边的点对距离不小于d可知,若有距离小于d的点对,一定是一个点在左边,一个点在右边。

并且,因为距离小于d,那这两点一定是在一个长宽分别为2d和d的矩形内,且分别位于两边的边长为d的正方形内。

由鸽笼定理知每个小正方形最多有4个点,那两个小正方形就有8个点,又因为有2个点重合,故只需要比较6个点。

换而言之,若比较排序好后的6个点还没找到比d小的距离值,那这个点一定不会是最近点对中的点,那么接下来取下一个点为基准点进行比较。


伪代码如下:


 
时间复杂度:
递归算法复杂度为2T(n/2),递归前的排序算法时间复杂度为O(nlog n),
总算法复杂度T(n) = 2T(n/2) + O(nlog n),
由主定理法及该算法a=b=2,nlog n = f(n) > nlogb a = n,
则该算法时间复杂度T(n)=O(f(n)) = O(nlog n)。


运行时间

 
可以看出,理论时间和实际时间基本一致,之所以会有细微的差别,是因为有一些代码耗时较小而在理论计算中被忽略。

可以明显的看出,分治法所用时间较少,运行速度较快。
 

实验总结

由实验数据和图像可知,在大数据量的处理过程中,分治法耗时远远小于暴力法,效率较高。因此,在处理大量数据时,应当选用分治法。

分治算法的核心思想就是将大的问题分割成小的问题来解决,以此达到提高运行效率的目的。

在本实验分治算法的合并中,很关键的一步就是判断出只需要最多比较6个点,极大地提高了算法效率,降低了运行时间。

实验伪代码

  1. for i=0 to n-1 //i的范围0~n-1
  2. for j=i to n-1 //j的范围i~n-1
  3. if(dist(d[i],d[j])<min) //如果小于min则更新min
  4. min=dist(d[i],d[j])
  5. divide(l,r) //divide分治进行递归
  6. if(l==r)//只有一个点时
  7. return max
  8. if(l+1==r)//只有两个点时
  9. retuen dist(d[l],d[r])
  10. mid=(l+r)/2 //分治递归
  11. juli1=divide(l,mid)
  12. juli2=divide(mid+1,r)
  13. juli=min(juli1,juli2)//取小值
  14. midd[]=d[mid.x+-juli]//取中间部分
  15. sort(midd) //排序
  16. for i=0 to geshu-1 //比较
  17. for j=i+1&&j<i+6
  18. if(dist(midd[i],midd[j])<juli)
  19. juli=dist(midd[i],midd[j])

 

实验代码 

  1. #include<iostream>
  2. #include<algorithm>
  3. #include<cmath>
  4. using namespace std;
  5. # define MAX 999999
  6. int n;//点的个数,十万到一百万
  7. double final;//最近点对距离
  8. struct dian
  9. {
  10. double x, y;
  11. dian() { x = y = -1; };
  12. dian(double xx, double yy)
  13. {
  14. x = xx; y = yy;
  15. }
  16. };
  17. dian* dians;//存储全部点集
  18. dian* middians;//存储中间部分点集
  19. dian d1, d2;//最终最近的两点
  20. double dist(dian d1, dian d2)//求两点间距离
  21. {
  22. return sqrt(pow(d1.x - d2.x, 2)+pow(d1.y - d2.y, 2));
  23. }
  24. bool cmpxy(dian a, dian b)//按先x后y排序
  25. {
  26. if(a.x!=b.x)
  27. return a.x < b.x;
  28. return a.y < b.y;
  29. }
  30. bool cmpy(dian a, dian b)//按y排序
  31. {
  32. return a.y < b.y;
  33. }
  34. void baoli()//暴力解法
  35. {
  36. int i, j;
  37. for(i=0;i<n;i++)
  38. for (j = i + 1; j < n; j++)
  39. {
  40. double juli = dist(dians[i], dians[j]);
  41. if (juli < final)
  42. {
  43. final = juli;
  44. d1 = dians[i];
  45. d2 = dians[j];
  46. }
  47. }
  48. }
  49. double divide(int l, int r)//分治法
  50. {
  51. double juli = MAX;
  52. if (l == r)//只有一个点时
  53. return juli;
  54. if (l + 1 == r)//只有两个点时
  55. {
  56. juli = dist(dians[l], dians[r]);
  57. if (juli < final)
  58. {
  59. juli = final;
  60. d1 = dians[l];
  61. d2 = dians[r];
  62. }
  63. //此处按y大小对二者进行排序 ,则之后就不需要按y大小sort中间部分 。将时间复杂度从O(nlog n)变为O(n)
  64. if(dians[l].y>dians[r].y)
  65. swap(dians[l],dians[r]);
  66. return juli;
  67. }
  68. int mid = (l + r) / 2;//分治递归
  69. double juli1 = divide(l, mid);
  70. double juli2 = divide(mid + 1, r);
  71. juli = min(juli1, juli2);//取左右两部分中最小的和final比较,若小于final则更新final
  72. if (juli < final)
  73. {
  74. final = juli;
  75. if (juli == juli1)
  76. {
  77. d1 = dians[l];
  78. d2 = dians[mid];
  79. }
  80. else
  81. {
  82. d1 = dians[mid + 1];
  83. d2 = dians[r];
  84. }
  85. }
  86. int i, j, k=0;//k在循环中做标记,循环结束后为数组长度
  87. for (i = l; i < r; i++)//以mid为轴,将其左右长度各为dist的点存入数组middians中
  88. {
  89. if (fabs(dians[mid].x - dians[i].x) <= juli)
  90. middians[k++] = dians[i];
  91. }
  92. //sort(middians, middians + k, cmpy);//按y进行排序 见第74行
  93. for(i=0;i<k;i++)
  94. for (j = i + 1; j<k&&j<i+6&&middians[j].y-middians[i].y<juli; j++)//由鸽笼原理知只需比较6个点,以此达到线性效率
  95. {
  96. double juli3 = dist(middians[i], middians[j]);
  97. if (juli3 < juli)
  98. {
  99. juli = juli3;
  100. if (juli < final)
  101. {
  102. final = juli;
  103. d1 = middians[i];
  104. d2 = middians[j];
  105. }
  106. }
  107. }
  108. return juli;
  109. }
  110. void show()
  111. {
  112. cout << "最近的点对为以下两点" << endl;
  113. cout << "(" << d1.x << "," << d1.y << ")" << endl;
  114. cout << "(" << d2.x << "," << d2.y << ")" << endl;
  115. cout << "最近距离大小为" << final << endl;
  116. }
  117. int main()
  118. {
  119. int i,k;
  120. for (k = 1; k <= 10; k++)//从十万到一百万
  121. {
  122. n = k*100000;
  123. cout << "点的个数n= " << n << endl;
  124. dians = new dian[n];//动态分配数组
  125. middians = new dian[n];
  126. srand(time(0));
  127. for (i = 0; i < n; i++) //分配随机数
  128. {
  129. double y = rand() / (double)RAND_MAX * n;
  130. double x = rand() / (double)RAND_MAX * n;
  131. dians[i]=dian(x, y);
  132. }
  133. sort(dians, dians + n, cmpxy);//按先x后y进行排序
  134. /*
  135. for (i = 0; i < n; i++) //输出随机生成的点对
  136. cout << dians[i].x << " " << dians[i].y << endl;
  137. cout << endl;
  138. */
  139. int qi, zhi;
  140. final = MAX;
  141. qi = clock();//记录运行时间
  142. baoli();
  143. zhi = clock();
  144. cout << "暴力法结果如下" << endl;
  145. show();
  146. cout << "耗时为" << zhi-qi << "ms" << endl << endl;
  147. final = MAX;
  148. qi = clock();
  149. divide(0, n - 1);
  150. zhi = clock();
  151. cout << "分治法结果如下" << endl;
  152. show();
  153. cout << "耗时为" << zhi-qi << "ms" << endl << endl;
  154. }
  155. }

(by 归忆)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/621334
推荐阅读
相关标签
  

闽ICP备14008679号