一个极其简单的最近点对问题线性扫描的O(nlog n)算法描述

写在前言:能搜到这个问题的人估计都是在做算法的作业吧。*请不要拿本文的内容去完成你的作业,老老实实去做分治法吧*

我姑且将这种算法称作:线性扫描算法。

第一点:本文主要阐述一种网上还搜不到的算法(至少截止我在写这篇文的时候没有看到)
第二点:本文只供参考,部分结果为统计数据而非严谨的理论推出数据。
第三点:欢迎在本文下方进行对该算法的进一步讨论,也希望各位指出文章存在的问题。
第四点:这个算法的思路很简单,肯定会有人想出来的,这里就把它阐述出来,以免以后有人看到过后会死命研究然后浪费大量时间,像我这样。。。
另: 图片中所有时间的数据都以ms为单位。

1. 问题描述:

给定任意一组,在所有点中,找到存在的两点间的最小距离

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l6R1oM43-1587526416166)(/Users/binll/Pictures/Xnip文件/Xnip2020-04-22_10-28-55.png)]

2. 蛮力法:

遍历所有的点,计算两点间的距离,得到最小距离,最终的算法复杂度是: O ( n 2 ) O(n^2) O(n2)。非常慢,可以检测算法的正确性。

3. 分治法:

分治法看上面链接就好了,时间复杂度是 O ( n log ⁡ n ) O(n\log n) O(nlogn)

具体点即是 T ( n ) = 2 T ( n / 2 ) + 2 O ( n ) + 5 O ( k ) T(n) = 2T(n/2) + 2O(n)+5O(k) T(n)=2T(n/2)+2O(n)+5O(k) k k k是常数。

4.线性扫描算法:
  • 先上结论:算法的时间复杂度为 O ( n log ⁡ n ) O(n\log n) O(nlogn)

具体点即是 T ( n ) = O ( n ) + O ( n log ⁡ n ) + O ( n ) + O ( 2.4 × n ) T(n) = O(n)+ O(n\log n) + O(n)+O(2.4 \times n) T(n)=O(n)+O(nlogn)+O(n)+O(2.4×n)

  • 和分治法的比较:线性扫描算法在数组长度到10000(万) - 10000000(百万)的区间内速度都是比分治法要快5倍左右。在万以下的数据时时间已经小于0ms了,百万以上分治法太慢了,就没测了。如图:

在这里插入图片描述

  1. 原理介绍:首先对整个点集进行X坐标排序,做到X坐标的有序,然后像蛮力法一样对每一个点与后面的点进行距离计算,不过加入一个剪枝条件:

  2. 剪枝条件介绍:当进行距离计算时如果发现两点(如点p1和点p2)之间的X坐标之差大于等于了当前的最短距离d,那么直接省略点P2以及P2后面所有的点,P1点扫描完毕,P1点向后移一位。因为此时P2以及P2后面所有的点,都不可能获得更小的最短的距离了。
    在这里插入图片描述

  3. 伪代码展示: (可以看到代码量只比蛮力法的算法多了十几行而已)

// 对X坐标进行运算,或者对Y坐标进行算法运算,参考 2020-04-24 更新的结果。
function LinearGetMin(Array) 
     quick sort Array
     for Point[i] in Array do
         for Point[j] in Array & j >= i + 1 & Point[j].x - Point[i].x < MinLength do
             GetLength(Point[i],Point[j]) 
             compare with MinLength
         end for
     end for
 end function

  1. 验证算法的正确性:

    • 有穷性:可以被终止
    • 确切性:每个步骤都有确切的定义
    • 输入项:一个点阵数组
    • 输出项:最短长度
    • 可行性:是可以在有限时间内完成的
  2. 算法的五个评定标准:

    • 时间复杂度: O ( n log ⁡ n ) O(n\log n) O(nlogn)
    • 空间复杂度: O ( n ) O(n) O(n)
    • 正确性:理论上是没有问题的,测试了近千组数据没问题
    • 可读性:见伪代码。
    • 健壮性:两点重合会出现问题,理论上这种情况不存在

    再多加一点:相同规模数据检测的稳定性是否是稳定的。经过大量数据检测发现,是稳定的。对700000长度的数据进行排序发现时间都在155ms附近,如图:

  3. 算法时间复杂度分析:

    1. 第一步:先对整个数组进行X轴的快速排序 [ 时间复杂度: O ( n log ⁡ n ) O(n\log n) O(nlogn) ]
    2. 第二步:进行横向的线性扫描 [ 时间复杂度: O ( n ) O(n) O(n) ],针对每一个点,进行一次纵向的扫描。
    3. 第三步:每个点的纵向扫描,纵向扫描进行的次数取决于两个因素:进行计算两点的x轴坐标值之差以及当前的最短长度d。
      在这里插入图片描述
    4. 第三步这一步有很高的数据敏感性,以我目前的知识储备,还不知道可以进行精确计算和证明的方法,所以我就进行了大量的数据检测。最后找到了一个规律:我计算出数据的 k = 纵 向 进 行 扫 描 的 平 均 次 数 数 组 长 度 k = \frac{纵向进行扫描的平均次数}{数组长度} k= ,最后发现这个 k ≈ 2.4 k \approx 2.4 k2.4 .
      如图:

    在这里插入图片描述

    1. 那我就推断 第三步:纵向扫描 的时间复杂度是: O ( 2.4 × n ) O(2.4 \times n) O(2.4×n)
    2. 所以得出整个算法的时间复杂度是: T ( n ) = O ( n log ⁡ n ) + O ( n ) + O ( 2.4 × n ) ≈ O ( n log ⁡ n ) T(n) = O(n \log n) + O(n) + O(2.4 \times n) \approx O(n\log n) T(n)=O(nlogn)+O(n)+O(2.4×n)O(nlogn)
    3. 最后拿 O ( n log ⁡ n ) O(n\log n) O(nlogn)的曲线进行验证:
      在这里插入图片描述
5. 写在最后:
  1. 这个算法是真的简单易懂,我觉得。
  2. 这个算法确实是比分治法要快的,但是针对这个算法不能得出精确的时间复杂度,也就导致了这个算法没有教育/传授目的,算一个歪门邪道的算法。
  3. 如果有人知道关于这个算法的一些计算的手段和知识,可以在评论区留言,嘻嘻。
  4. 注:给老师讲完PPT后,老师说可以用面积+概率来求解纵向比较的问题(cy后续更新吧)

* 2020-04-24更新:
  • 一位同学给我讲述了一种会导致最终算法时间复杂度结果是 O ( n 2 ) O(n^2) O(n2)的特殊情况:当X轴坐标值很小,而Y轴坐标值很大的时候,就会形成如下图所示的长宽比不正常的矩形,这时候X轴坐标之差很小,就不应该拿X坐标来做排序和比较的基准了:

在这里插入图片描述

  • 此时的解决办法也很简单,只需要把矩阵给转过来使得长大于宽就好了,所以上面这种情况对Y轴排序就好了,所有对X坐标的处理全部改为对Y坐标的处理。
  • 那么对于算法来讲:在进行X坐标的快速排序之前,就应该先对点进行一次扫描,来获得Y轴最大值y_max和最小值y_min以及X轴的最大值x_max和最小值x_min,最后发现如果y_max - y_min >= x_max - x_min就以Y为算法的基准轴,否则以X为算法的基准轴。算法时间复杂度变为: T ( n ) = O ( n ) + O ( n log ⁡ n ) + O ( n ) + O ( 2.4 × n ) T(n) = O(n)+ O(n \log n) + O(n) + O(2.4 \times n) T(n)=O(n)+O(nlogn)+O(n)+O(2.4×n),基本没有影响
  • 总之就是一定要让矩形的长要比宽要大!

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值