写在前言:能搜到这个问题的人估计都是在做算法的作业吧。*请不要拿本文的内容去完成你的作业,老老实实去做分治法吧* 。
我姑且将这种算法称作:线性扫描算法。
第一点:本文主要阐述一种网上还搜不到的算法(至少截止我在写这篇文的时候没有看到)
第二点:本文只供参考,部分结果为统计数据而非严谨的理论推出数据。
第三点:欢迎在本文下方进行对该算法的进一步讨论,也希望各位指出文章存在的问题。
第四点:这个算法的思路很简单,肯定会有人想出来的,这里就把它阐述出来,以免以后有人看到过后会死命研究然后浪费大量时间,像我这样。。。
另: 图片中所有时间的数据都以ms为单位。
1. 问题描述:
给定任意一组,在所有点中,找到存在的两点间的最小距离
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了,百万以上分治法太慢了,就没测了。如图:
-
原理介绍:首先对整个点集进行X坐标排序,做到X坐标的有序,然后像蛮力法一样对每一个点与后面的点进行距离计算,不过加入一个剪枝条件:
-
剪枝条件介绍:当进行距离计算时如果发现两点(如点p1和点p2)之间的X坐标之差大于等于了当前的最短距离d,那么直接省略点P2以及P2后面所有的点,P1点扫描完毕,P1点向后移一位。因为此时P2以及P2后面所有的点,都不可能获得更小的最短的距离了。
-
伪代码展示: (可以看到代码量只比蛮力法的算法多了十几行而已)
// 对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
-
验证算法的正确性:
- 有穷性:可以被终止
- 确切性:每个步骤都有确切的定义
- 输入项:一个点阵数组
- 输出项:最短长度
- 可行性:是可以在有限时间内完成的
-
算法的五个评定标准:
- 时间复杂度: O ( n log n ) O(n\log n) O(nlogn)
- 空间复杂度: O ( n ) O(n) O(n)
- 正确性:理论上是没有问题的,测试了近千组数据没问题
- 可读性:见伪代码。
- 健壮性:两点重合会出现问题,理论上这种情况不存在
再多加一点:相同规模数据检测的稳定性是否是稳定的。经过大量数据检测发现,是稳定的。对700000长度的数据进行排序发现时间都在155ms附近,如图:
-
算法时间复杂度分析:
- 第一步:先对整个数组进行X轴的快速排序 [ 时间复杂度: O ( n log n ) O(n\log n) O(nlogn) ]
- 第二步:进行横向的线性扫描 [ 时间复杂度: O ( n ) O(n) O(n) ],针对每一个点,进行一次纵向的扫描。
- 第三步:每个点的纵向扫描,纵向扫描进行的次数取决于两个因素:进行计算两点的x轴坐标值之差以及当前的最短长度d。
- 第三步这一步有很高的数据敏感性,以我目前的知识储备,还不知道可以进行精确计算和证明的方法,所以我就进行了大量的数据检测。最后找到了一个规律:我计算出数据的
k
=
纵
向
进
行
扫
描
的
平
均
次
数
数
组
长
度
k = \frac{纵向进行扫描的平均次数}{数组长度}
k=数组长度纵向进行扫描的平均次数 ,最后发现这个
k
≈
2.4
k \approx 2.4
k≈2.4 .
如图:
- 那我就推断 第三步:纵向扫描 的时间复杂度是: O ( 2.4 × n ) O(2.4 \times n) O(2.4×n)
- 所以得出整个算法的时间复杂度是: 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)
- 最后拿
O
(
n
log
n
)
O(n\log n)
O(nlogn)的曲线进行验证:
5. 写在最后:
- 这个算法是真的简单易懂,我觉得。
- 这个算法确实是比分治法要快的,但是针对这个算法不能得出精确的时间复杂度,也就导致了这个算法没有教育/传授目的,算一个歪门邪道的算法。
- 如果有人知道关于这个算法的一些计算的手段和知识,可以在评论区留言,嘻嘻。
- 注:给老师讲完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),基本没有影响 - 总之就是一定要让矩形的长要比宽要大!