平面上最近点对

        在二维平面上的n个点中,如何快速的找出最近的一对点,就是最近点对问题。

     一种简单的想法是暴力枚举每两个点,记录最小距离,显然,时间复杂度为O(n^2)。

    在这里介绍一种时间复杂度为O(nlognlogn)的算法。其实,这里用到了分治的 思想。将所给平面上n个点的集合S分成两个子集S1和S2,每个子集中约有n/2个点。然后在每个子集中递归地求最接近的点对。在这里,一个关键的问题是 如何实现分治法中的合并步骤,即由S1和S2的最接近点对,如何求得原集合S中的最接近点对。如果这两个点分别在S1和S2中,问题就变得复杂了。

    为了使问题变得简单,首先考虑一维的情形。此时,S中的n个点退化为x轴上的n个实数x1,x2,...,xn。最接近点对即为这n个实数中相差最小的两个实数。显然可以先将点排好序,然后线性扫描就可以了。但我们为了便于推广到二维的情形,尝试用分治法解决这个问题。

    假设我们用m点将S分为S1和S2两个集合,这样一来,对于所有的p(S1中的点)和q(S2中的点),有p<q。

    递归地在S1和S2上找出其最接近点对{p1,p2}和{q1,q2},并设

d = min{ |p1-p2| , |q1-q2| }

    由此易知,S中最接近点对或者是{p1,p2},或者是{q1,q2},或者是某个{q3,p3},如下图所示。



 

    如果最接近点对是{q3,p3},即|p3-q3|<d,则p3和q3两者与m的距离都不超过d,且在区间(m-d,d]和(d,m+d]各有且仅有一个点。这样,就可以在线性时间内实现合并。

    此时,一维情形下的最近点对时间复杂度为O(nlogn)。

    在二维情形下,类似的,利用分治法,但是难点在于如何实现线性的合并?



 

    由上图可见,形成的宽为2d的带状区间,最多可能有n个点,合并时间最坏情况下为n^2,。但是,P1和P2中的点具有以下稀疏的性质,对于P1中的任意一点,P2中的点必定落在一个d X 2d的矩形中,且最多只需检查六个点(鸽巢原理)。

    这样,先将带状区间的点按y坐标排序,然后线性扫描,这样合并的时间复杂度为O(nlogn),几乎为线性了。

算法描述:已知集合S中有n个点,分治法的思想就是将S进行拆分,分为2部分求最近点对。算法每次选择一条垂线L,将S拆分左右两部分为SL和SR,L一般取点集S中所有点的中间点的x坐标来划分,这样可以保证SL和SR中的点数目各为n/2,

(否则以其他方式划分S,有可能导致SL和SR中点数目一个为1,一个为n-1,不利于算法效率,要尽量保持树的平衡性)

依次找出这两部分中的最小点对距离:δL和δR,记SL和SR中最小点对距离δ = min(δL,δR),如图1:

 

   

     以L为中心,δ为半径划分一个长带,最小点对还有可能存在于SL和SR的交界处,如下图2左图中的虚线带,p点和q点分别位于SL和SR的虚线范围内,在这个范围内,p点和q点之间的距离才会小于δ,最小点对计算才有意义。

    

 

Figure 2

 

 

      对于SL虚框范围内的p点,在SR虚框中与p点距离小于δ的顶多只有六个点,就是图二右图中的2个正方形的6的顶点。这个可以反推证明,如果右边这2个正方形内有7个点与p点距离小于δ,例如q点,则q点与下面正方形的四个顶点距离小于δ,则和δ为SL和SR中的最小点对距离

相矛盾。因此对于SL虚框中的p点,不需求出p点和右边虚线框内所有点距离,只需计算SR中与p点y坐标距离最近的6个点,就可以求出最近点对,节省了比较次数。

(否则的话,最坏情形下,SR虚框中有可能会有n/2个点,对于SL虚框中的p点,每次要比较n/2次,浪费了算法的效率

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>/*核心是分治算法1. 分别根据点的 x,y 值进行排序2. 在 x 轴上划一道垂线, 将点均分成两半3. 假设最近点对都在左/右部分, 递归计算左/右半部分的最短距离并返回较小值 dis4. 假设最近点对分别在左右两个部分, 横跨中心的竖线. 中心线为中心, 2*dis 为宽度画一个矩形, 横跨中心线的最近点对 candidate 都在这个矩形内.将这些点按照 y 值的大小加入到数组中. 遍历数组中的点, 将该点与其后的 7 个点计算距离, 返回最小距离5. 为什么仅和 7 个点作对比呢. 因为已经假设 dis 是左右不分最近点对的最小值,这就说明在一个长(宽)为 dis 的正方形内, 至多有 4 个点. 长为 dis*2, 宽为 dis 的长方形至多 8 个.
*/const double INF = 1e20;//10^20,科学计数方法 
const int N = 100005;struct Point
{double x;double y;
}point[100005]; //point用来存储输入的坐班点 
int n;
int tmpt[100005];//tmpt[]用来保存,中间区域点的顺序 int cmpxy(const void *a, const void *b) //对所有的点进行横坐标升序排序 
{                                       //横坐标相同的按纵坐标升序 Point *c=(Point *)a;Point *d=(Point *)b;if(c->x!= d->x)return c->x-d->x;return c->y-d->y;
}int cmpy(const void *a, const void * b) //中间区域排序 
{   //对距离mid点横向距离少于d的点,进行纵坐标升序排序  int c=*(int*)a, d=*(int*)b;return point[c].y - point[d].y;
}double min(double a, double b)
{return a < b ? a : b;
}double dis(int i, int j)//计算两点间的距离 
{return sqrt((point[i].x-point[j].x)*(point[i].x-point[j].x)+(point[i].y-point[j].y)*(point[i].y-point[j].y));
}double Closest_Pair(int left, int right)//求解最近点对 
{double d = INF;if(left==right)   //二分到区间只有一个点时,返回 return d;if(left + 1 == right)//二分到区间只有两个点时,返回两点间的距离 return dis(left, right);int mid = (left+right)/2; //取中间位置 double d1 = Closest_Pair(left,mid);//求左边最小距离 double d2 = Closest_Pair(mid+1,right);//求右边最小距离 d = min(d1,d2);  //   printf("d1=%.4f d2=%.4f d01=%.4f\n",d1,d2,d);int i,j,k=0;//分离出距离中心横向宽度为d的点区间 for(i=left;i<=right;i++)  {if(fabs(point[mid].x-point[i].x)<=d)tmpt[k++]=i;}qsort(tmpt,k,sizeof(int),cmpy);//分离出来的区间点,纵坐标进升序排序 //   for(i=0;i<k;i++) printf("tmpt[%d]=%d ",i,tmpt[i]);//   printf("分界\n");//线性扫描,求解中间位置两侧的最小两点距离 for(i = 0;i<k;i++){for(j=i+1;j<k;j++)//理解!!{if(point[tmpt[j]].y-point[tmpt[i]].y<d){double d3 = dis(tmpt[i],tmpt[j]);//              printf("d3=%.4f\n",d3);if(d > d3)d = d3;}}}
//   printf("d02=%.4f\n",d);return d;}int main()
{freopen("nearest.in","r",stdin);freopen("nearest.out","w",stdout); scanf("%d",&n);for(int i = 0; i < n; i++)scanf("%lf%lf",&point[i].x,&point[i].y);qsort(point,n,sizeof(Point),cmpxy);/*     for(int i = 0; i < n; i++)printf("%lf %lf\n",point[i].x,point[i].y);*/printf("%.4lf\n",Closest_Pair(0,n-1));return 0;
}


转载于:https://www.cnblogs.com/tham/p/6827477.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/460331.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

分词之后的如何做特征选择_特征选择怎么做?这篇文章告诉你

作者 | Pier Paolo Ippolito翻译 | Skura编辑 | 唐里原文标题&#xff1a;Feature Selection Techniques据《福布斯》报道&#xff0c;每天大约会有 250 万字节的数据被产生。然后&#xff0c;可以使用数据科学和机器学习技术对这些数据进行分析&#xff0c;以便提供分析和作出…

Shell脚本中循环语句for,while,until用法

循环语句:BashShell中主要提供了三种循环方式&#xff1a;for、while和until。一、for循环for循环的运作方式&#xff0c;是讲串行的元素意义取出&#xff0c;依序放入指定的变量中&#xff0c;然后重复执行含括的命令区域&#xff08;在do和done 之间&#xff09;&#xff0c;…

我的扫描器设计

Web扫描器SeachPlan的设计与实现 姓名 李波 2014年6月 中图分类号&#xff1a;TP311. UDC分类号&#xff1a;004. Web漏洞扫描器SeachPlan的设计与实现 作 者 姓 名 李波 学 院 名 称 软件学院 指 导 教 师 XXX 答辩委员会主席 XXX …

git管理账户忘记了_强制找回GitLab管理员账户密码的方法

01. 概述如果gitlab的密码忘记了&#xff0c;由于没有邮箱服务支持&#xff0c;所以可以通过后台直接操作git-shell&#xff0c;先找回管理员的密码。操作步骤如下02. 环境使用docker部署的gitlab&#xff0c;因此需要多执行一步&#xff0c;如果是传统安装方法&#xff0c;不用…

无名管道pipe使用方法

有名管道pipe函数:int pipe(int filedes[2]); 下面程序通过创建进程&#xff0c;父进程写入数据&#xff0c;子进程读取数据&#xff0c;从管道中读取数据。 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #i…

交换2个数值的方法

如何交换2个变量的值&#xff1f;一般来说&#xff0c;想到的方法是再找一个临时变量&#xff0c;通过一种顺序存放的方式来达到交换值的效果。 比如有a,b这2个变量。取同类型的变量temp。 temp a ; a b ; b temp ; 这样就完成了交换。但这样做的话会多使用一个temp的内存。…

mysql的char和varchar_MySQL的char和varchar

1.varchar类型的变化MySQL 数据库的varchar类型在4.1以下的版本中的最大长度限制为255&#xff0c;其数据范围可以是0~255或1~255(根据不同版本数据库来定)。在 MySQL5.0以上的版本中&#xff0c;varchar数据类型的长度支持到了65535&#xff0c;也就是说可以存放65532个字节的…

【问题收集·知识储备】Xcode只能选择My Mac,不能选择模拟器如何解决?

网友问题:请问打开一个应用&#xff0c;只能选择My Mac&#xff0c;不能选择模拟器如何解决&#xff1f; 答案: 下面将问答过程记录如下&#xff1a; CHENYILONG Blog请问打开一个应用&#xff0c;只能选择My Mac&#xff0c;不能选择模拟器如何解决&#xff1f; 网友的问题 09…

typedef的使用方法

typedef是在计算机编程语言中用来为复杂的声明定义简单的别名&#xff0c;与宏定义有些差异。它本身是一种存储类的关键字&#xff0c;与auto、extern、mutable、static、register等关键字不能出现在同一个表达式中。 使用typedef为现有类型创建别名&#xff0c;定义易于记忆的…

SQL Server CLR全功略之一---CLR介绍和配置

Microsoft SQL Server 现在具备与 Microsoft Windows .NET Framework 的公共语言运行时 (CLR) 组件集成的功能。CLR 为托管代码提供服务&#xff0c;例如跨语言集成、代码访问安全性、对象生存期管理以及调试和分析支持。对于 SQL Server 用户和应用程序开发人员来说&#xff0…

mysql select 子查询_SELECT中常用的子查询操作

MySQL中的子查询 是在MySQL中经常使用到的一个操作,不仅仅是用在DQL语句中,在DDL语句、DML语句中也都会常用到子查询。 子查询的定义: 子查询是将一个查询语句嵌套在另一个查询语句中; 在特定情况下,一个查询语句的条件需要另一个查询语句来获取,内层查询(inner query)语…

视频教程--ASP.NET MVC 使用 Petapoco 微型ORM框架+NpgSql驱动连接 PostgreSQL数据库

说好的给园子里的朋友们录制与《ASP.NET MVC 使用 Petapoco 微型ORM框架NpgSql驱动连接 PostgreSQL数据库》 这篇博客相对应的视频&#xff0c;由于一个月一来没有时间录制&#xff0c;今天我兑现了给朋友们的承诺。。 本次视频教程的目录为 视频、代码、资料&#xff0c;其中视…

ARM中各始终之间的关系,FCLK HCLK PCLK的关系

ChangeMPllValue((mpll_val>>12)&0xff, (mpll_val>>4)&0x3f, mpll_val&3); ChangeClockDivider(key, 12); 1&#xff09;FLCK、HCLK和PCLK的关系 S3C2440有三个时钟FLCK、HCLK和PCLK s3c2440官方手册上说P7-8写到&#xff1a; FCLK is used by ARM920T…

CSS垂直居中总结

原文:CSS垂直居中总结工作中遇到垂直居中问题&#xff0c;特此总结了一下几种方式与大家分享。本文讨论的垂直居中仅支持IE8 1、使用绝对定位垂直居中 <div class"container"><!--<div class"floater"></div>--><div class&quo…

mysql添加表字段脚本_mysql数据库修改字段及新增字段脚本

1.修改字段的长度ALTER TABLE MODIFY COLUMN 字段名 数据类型(修改后的长度)例句&#xff1a;ALTER TABLE test_table MODIFY COLUMN id INT(20)2.修改字段的名称alter table change 。例句&#xff1a;ALTER TABLE test_table CHANGE attence_name NAME VARCHAR(20)3.新增字…

java transient简单介绍

我们都知道一个对象仅仅要实现了Serilizable接口&#xff0c;这个对象就能够被序列化&#xff0c;java的这样的序列化模式为开发人员提供了非常多便利&#xff0c;我们能够不必关系详细序列化的过程&#xff0c;仅仅要这个类实现了Serilizable接口&#xff0c;这个的全部属性和…

struct和typedef struct区别

分三块来讲述&#xff1a;   1 首先&#xff1a;//注意在C和C里不同     在C中定义一个结构体类型要用typedef: typedef struct Student{int a;}Stu; 于是在声明变量的时候就可&#xff1a;Stu stu1;(如果没有typedef就必须用struct Student stu1;来声明)     这里…

单元测试代码:SpringTest+JUnit

2019独角兽企业重金招聘Python工程师标准>>> /*** JUnit单元测试父类&#xff0c;配置了Spring的基础环境。 <br/>* 可以作为Controller、Service、Dao单元测试的父类。* * author leiwenfansunion.cn*/ public class JUnitTestBase {public static XmlWebApp…

mysql类 php100_php mysql 类

1 <?php2 /**3 * MySQL 数据库操作工具类, 方便数据库操作.4 * 示例见底部注释.5 * author: fuck me every day6 */7 class Mysql{8 var $conn;9 var $query_list array();10 public $query_count 0;1112 public function __construct($c){13 if(!isset($c[port])){14 $c…

SQL Server 索引和表体系结构(三)

SQL Server 索引和表体系结构&#xff08;三&#xff09; 原文:SQL Server 索引和表体系结构&#xff08;三&#xff09;包含列索引 概述 包含列索引也是非聚集索引&#xff0c;索引结构跟聚集索引结构是一样&#xff0c;有一点不同的地方就是包含列索引的非键列只存储在叶子节…