如何从二维平面n个点中寻找距离最近两个点?
如何理解分治算法
什么是分治算法?简单来说就是“分而治之”,也就是将原问题划分成n个规模较小的,并且结构与原问题相似的子问题,然后去递归地解决这些子问题,最后再合并其结果,就得到原问题的解。
对于分治算法来说,一般适合用递归来实现。分治算法的递归实现中,每一次递归都会涉及如下三个操作。
- 分解:将原问题分解成一系列子问题。
- 解决:递归地求解各个子问题。
- 合并:将子问题的结果合并成原问题。
分治算法的问题模型
对于可以采用分治算法来解决的问题,一般都需要满足以下几个条件:
- 原问题与分解成的子问题具有相同的模式。
- 原问题分解成的子问题可以独立求解,子问题之间没有相关性。
- 具有分解终止条件,也就是说,当问题足够小时,可以直接求解。
- 可以将子问题合并成原问题,而这个合并操作的复杂度不能太高,否则就起不到减小算法总体复杂度的效果了。
下面我们来结合一个例子来看一下分治算法的应用。我们来思考这么一个问题,在给定一组在二维空间中的n个点,如何快速找出距离最近的两点呢?最直观的想法就是通过遍历所有的点,然后求出所有点之间的距离,最后选出这些距离中的最小值对应的点,但是这种算法的时间复杂度是O(n*n),那有没有更快的方法呢?那就是分治算法。
为了方便起见,我们把N设置为2的k次幂,即N=2^k,k为正整数。下面我们来看一下这个问题是否满足分治算法的问题模型。
我们可以把问题N拆分成两个子问题,每个子问题等于原问题的一半。我们在二维平面中画一条垂线e,正好把N个点按照x轴位置拆分成2半(这个过程需要按照x轴排序,取中间的点),使得e的左右两边都有n/2个点。假设我们对e的左半边部分递归的求出的最近点对的距离为ld,e的右半边部分递归的求出的最近点对的距离为rd,接下来我们取出这两个距离中的最小值d=min(ld,rd),并且在e-d和e+d的位置上画两条垂线。这样一来,我们把二维空间划分成了3部分。如下图所示:
这样划分后,最近点对的出现位置只能有以下三种可能。
- 两个节点都在左边区域。
- 一个节点在左,一个节点在右。
- 两个节点都在右边。
其中,1和3我们已经在求解子问题的时候已经解决了,现在我们只需要求解第2个问题就好了,即在mid区域进行搜索。我们下面来看如何在mid区域进行搜索,我们需要把这个带状区域内的所有节点按照Y轴递增排序(我们可以在初始化的时候就缓存起来,后面直接使用就好),从Y排序最小的节点开始,我们连续检查带状区域内的每个点,计算所有比它的Y轴更大的点之间的距离,尝试找出距离比d更小的点对。
假设我们需要对节点p进行比较,我们考虑这样的一个空间,他是通过8个正方形(d/2*d/2)组成的一个长方形,p节点位于这个长方形的底边上,
我们只需要判断这个区域内的所有节点即可,因为一旦Y轴差距超过了d,就算x轴之差为0也会大于我们之前的距离d,所以不可能找出比 d 距离更小的点对。并且我们针对一个点,事实上只需要最多对比 7 次就能找出所有可能小于 d 的点对,因为每个小格子内部只可能出现1个节点,因为小格子内部的极限距离为 d / sqrt(2),也就是正方形的对角线,但是这个距离显然小于 d,如果这个正方形内部存在这样一个节点。那么之前对于 d 是左右区域内最小的点对距离的定义就被破坏了。所以不可能存在。
综上所述:这个问题是符合分治算法的问题模型的。下面我们来看一下代码实现。
`
import math
class Point:
def __init__(self,x,y):
self.x=x
self.y=y
def __str__(self):
return 'x='+str(self.x)+',y='+str(self.y)
class RecentPoint:
sortByXPoints = []
sortByYPoints = []
# p1=None
# p2=None
def getDistance(self, p1, p2):
xDis = (p1.x - p2.x) ** 2
yDis = (p1.y - p2.y) ** 2
return math.sqrt(xDis + yDis)
def findRecentPoint(self,data):
self.sortByXPoints=sorted(data,key=lambda point:point.x)
self.sortByYPoints=sorted(data,key=lambda point:point.y)
return self._findRecentPoint(0, len(data)-1)
def _findRecentPoint(self, p, q):
#区域内只有两对节点
if (q-p)<=1:
return self.getDistance(self.sortByXPoints[p], self.sortByXPoints[q])
middle=math.floor((p+q)/2)
ld=self._findRecentPoint(p, middle)
rd=self._findRecentPoint(middle+1, q)
# if(ld<rd):
# d=ld
# self.p1 = self.sortByXPoints[p]
# self.p2 = self.sortByYPoints[middle]
# else:
# d = rd
# self.p1 = self.sortByXPoints[middle+1]
# self.p2 = self.sortByYPoints[q]
d=min(ld,rd)
#中心点
e=self.sortByXPoints[middle].x + (self.sortByXPoints[middle+1].x - self.sortByXPoints[middle].x) / 2
LeftEdge = e - d
RightEdge = e + d
#接下来我们检查已 X 轴坐标 e 为中心点 从 e - d 开始 e + d 结束的带状区域内去检测最近点
#我们从中筛选所有带状区域内的点,并按照 Y坐标 的递增排序进行排序
insidePoint=[]
for point in self.sortByYPoints:
if(point.x>LeftEdge and point.x<RightEdge):
insidePoint.append(point)
#开始对比节点,寻找是否比d更短
for i in range(len(insidePoint)):
for j in range(1,8):
if(i+j>=len(insidePoint)):
break;
dis=self.getDistance(insidePoint[i],insidePoint[i+j])
if(dis<d):
d=dis
# self.p1=insidePoint[i]
# self.p2=insidePoint[i+j]
return d
data=[Point(1,6),Point(3, 4),Point(2, 5),Point(4, 8)]
s=RecentPoint()
print(s.findRecentPoint(data))
# print(s.p1)
# print(s.p2)
复制代码
` 今天的分享就到这里,更多硬核知识,请关注公众号,如果需要pdf文件,可以私信我哦。
如何从二维平面n个点中寻找距离最近两个点?的更多相关文章
- ZXing二维码生成在Unity3D中出错,数组超出界限的解决办法
错误截图: IndexOutOfRangeException: Array index is out of range.ZXing.Color32Renderer.Render (ZXing.Comm ...
- ASP.NET 生成二维码(采用ThoughtWorks.QRCode和QrCode.Net两种方式)
最近做项目遇到生成二维码的问题,发现网上用的最多的是ThoughtWorks.QRCode和QrCode.Net两种方式.访问官网看着例子写了两个Demo,使用过程中发现两个都挺好用的,Thought ...
- 使用JavaScript生成二维码教程-附qrcodejs中文文档
使用javascript生成二维码 依赖jquery 需要使用到的库 https://github.com/davidshimjs/qrcodejs DIV <div id="qrco ...
- spring boot高性能实现二维码扫码登录(中)——Redis版
前言 本打算用CountDownLatch来实现,但有个问题我没有考虑,就是当用户APP没有扫二维码的时候,线程会阻塞5分钟,这反而造成性能的下降.好吧,现在回归传统方式:前端ajax每隔1秒或2秒发 ...
- python如何删除二维或者三维数组/列表中某维的空元素
如题,个人在使用python进行数据预处理过程中出现的问题,抽象成删除三维列表中某维为空的问题. 一.首先来看一下三维数组/列表的结构 仔细看下图就会很清楚了: 轴0即是去除第一个外括号后第一层(我把 ...
- 【牛客挑战赛30D】小A的昆特牌(组合问题抽象到二维平面)
点此看题面 大致题意: 有\(S\)张无编号的牌,可以将任意张牌锻造成\(n\)种步兵或\(m\)种弩兵中的一种,求最后步兵数量大于等于\(l\)小于等于\(r\)的方案数. 暴力式子 首先我们来考虑 ...
- 存在一个足够大的二维数组,每个数组中的值都是整数,使用javascript如何实现按每个数组中的平均值,从大到小排序这个二维数组?
这是牛客网上的一道题~ 题意:对数组排序,顺序是按照数组的平均值,即按照一个元素和平均值相减的绝对值的大小来排序...本例按这个绝对值递增排序 解题思想:先求出这个数组的平均值,如果 a<b,那 ...
- 生成二维码项目pom.xml中QRCode依赖报错
pom.xml报错如下: 解决方法: 1.将QRCode.jar包下载到E盘下 网盘下载地址: 链接:https://pan.baidu.com/s/1sY1NK5ekCkNH0uqraZMnKw 提 ...
- PHP二维码生成的方法(google APi,PHP类库,libqrencode等)
原文地址: http://blog.csdn.net/liuxinmingcode/article/details/7910975 ================================== ...
随机推荐
- 在vue中下拉框切换事件中改新建表单中的一个值,页面不显示
事件中改新建表单中的一个值,页面不显示,当另一个对象值发生改变时,这个页面上的值才会显示 由于新建表单是弹窗,在弹出时会重新给每个字段重新赋值,在赋值时没给这个字段赋值(常见新加功能时,加了一个字段, ...
- CosId 1.0.3 发布,通用、灵活、高性能的分布式 ID 生成器
CosId 通用.灵活.高性能的分布式 ID 生成器 介绍 CosId 旨在提供通用.灵活.高性能的分布式系统 ID 生成器. 目前提供了俩大类 ID 生成器:SnowflakeId (单机 TPS ...
- 11、函数(def)
11.1.函数: 函数即变量 函数的作用域只跟函数声明时定义的作用域有关,跟函数的调用位置无任何关系 1.函数格式: def test(x): ''' 2*x+1 :param x:整形数字 :ret ...
- [源码解析] 深度学习分布式训练框架 horovod (11) --- on spark --- GLOO 方案
[源码解析] 深度学习分布式训练框架 horovod (11) --- on spark --- GLOO 方案 目录 [源码解析] 深度学习分布式训练框架 horovod (11) --- on s ...
- 用“kill”命令来强制终结某个行为或失常的应用和命令
首先让我们先了解"kill"命令,无论你使用哪种操作系统,你一定会遇到某个行为失常的应用,它把自己锁死并拒绝关闭.在Linux(还有Mac),你可以用一个" ...
- hadoop源码_hdfs启动流程_2_DataNode
执行start-dfs.sh脚本后,集群是如何启动的? 本文阅读并注释了start-dfs脚本,以及datanode的启动主要流程流程源码. DataNode 启动流程 脚本代码分析 start-df ...
- 如何处理RabbitMQ 消息堆积和消息丢失问题
消息堆积 解决方案: 增加消费者或后台相关组件的吞吐能力 增加消费的多线程处理 根据不同的业务实现不同的丢弃任务,选择不同的策略淘汰任务 默认情况下,RabbitMQ消费者为单线程串行消费,设置并行消 ...
- C++ 11 多线程初探-std::memory_order
std::memory_order(可译为内存序,访存顺序) 动态内存模型可理解为存储一致性模型,主要是从行为(behavioral)方面来看多个线程对同一个对象同时(读写)操作时(concurren ...
- MySQL数据库性能优化该如何入手
今天小杨给大家分享一篇关于数据库查询优化,数据库的操作越来越成为整个应用的性能瓶颈了,这点对于Web应用尤其明显.关于数据库的性能,这并不只是DBA才需要担心的事,而这更是我们程序员需要去关注的事情. ...
- commons-collections利用链学习总结
目录 1 基础 ConstantTransformer InvokeTransformer ChainedTransformer LazyMap TiedMapEntry TransformingCo ...