算法 1
创建一个 500x500 二维数组,其中每个单元格包含该单元格中粒子数的计数。然后将该数组与 50x50 内核卷积,生成的数组将包含每个单元格中 50x50 区域中的粒子数。然后找到具有最大值的单元格。
如果您使用 50x50 的框作为区域,内核可以分解为两个独立的卷积,每个轴一个。生成的算法是 O(n^2) 空间和时间,其中 n 是您正在搜索的 2D 空间的宽度和高度。
提醒一下,一个带有 boxcar 函数的一维卷积可以在 O(n) 的时间和空间内完成,并且可以就地完成。设 x(t) 为 t=1..n 的输入,设 y(t) 为输出。为 tn 定义 x(t)=0 和 y(t)=0。将内核 f(t) 定义为 0..d-1 的 1 和其他地方的 0。卷积的定义给了我们以下公式:
y(t) = sum i x(t-i) * f(i) = sum i=0..d-1 x(t-i)
这看起来需要时间 O(n*d),但我们可以将其重写为循环:
y(t) = y(t-1) + x(t) - x(t-d)
这说明一维卷积是O(n),与d无关。要执行二维卷积,您只需对每个轴执行一维卷积。之所以可行,是因为可以分解 boxcar 内核:通常,大多数内核无法分解。高斯核是另一个可以分解的核,这也是图像编辑程序中的高斯模糊速度如此之快的原因。
对于您指定的数字类型,这将非常快。 500x500 是一个极小的数据集,你的计算机最多可以在几毫秒内检查 202,500 个区域。您将不得不问自己,是否值得额外花费数小时、数天或数周的时间来进一步优化。
这和justhalf的解法一样,只是由于分解了卷积,所以区域大小不影响算法的速度。
算法 2
假设至少有一个点。不失一般性,将二维空间视为整个平面。令 d 为区域的宽度和高度。设 N 为点数。
引理:存在一个密度最大的区域,其左边缘有一个点。
证明:令 R 为最大密度区域。设 R' 是同一个区域,向右平移 R 的左边缘和 R 中最左边的点之间的距离。R 中的所有点也必须位于 R' 中,因此 R' 也是最大密度的区域。
算法
将所有点插入到 K-D 树中。这可以在 O(N log2 N) 时间内完成。
对于每个点,考虑宽度为 d 和高度为 2d 的区域,其中该点位于该区域的左边缘。将此区域称为 R。
在 KD 树中查询区域 R 中的点。将此集合称为 S。这可以在 O(N1/2+|S|) 时间内完成。
找到 R 的 dxd 子区域,其中包含 S 中最大数量的点。这可以在 O(|S| log |S|) 时间内完成,方法是按 y 坐标对 S 进行排序,然后执行线性扫描.
生成的算法的时间为 O(N3/2 + N |S| log |S|)。
比较
当密度高时,算法#1 优于算法#2。算法#2 仅在粒子密度非常低的情况下具有优势,而算法#2 的优势密度会随着总板尺寸的增加而降低。
请注意,可以认为连续情况的密度为零,此时只有算法 #2 有效。