9 几何算法之最近点对问题

在本章中,我们将探讨计算几何中的一个经典问题:最近点对问题。该问题旨在找到平面或空间中距离最近的一对点。它在诸多应用中具有重要意义,如碰撞检测、路径规划及数据压缩等。

问题定义

给定一个点集 $P = {p_1, p_2, \ldots, p_n}$,每个点 $p_i$ 在平面中的坐标为 $(x_i, y_i)$,我们需要找到两个点 $p_i$ 和 $p_j$,使得它们之间的距离最小,即求解以下最小值:

$$ d(p_i, p_j) = \sqrt{(x_i - x_j)^2 + (y_i - y_j)^2} $$

朴素方法

最简单的方法是使用一个双重循环来检查所有点对的距离,时间复杂度为 $O(n^2)$。具体实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def naive_closest_pair(points):
n = len(points)
min_distance = float('inf')
closest_pair = (None, None)

for i in range(n):
for j in range(i + 1, n):
distance = ((points[i][0] - points[j][0]) ** 2 +
(points[i][1] - points[j][1]) ** 2) ** 0.5
if distance < min_distance:
min_distance = distance
closest_pair = (points[i], points[j])

return closest_pair, min_distance

然而,该算法对于大规模数据集效率极低,因此我们需要更高效的解决方案。

分治法

思路

我们可以利用分治法来减少时间复杂度。该方法的基本思路是将点集分为两半,然后递归地找到每一半中的最近点对。同时,我们还需考虑可能跨越两个子集的最近点对。

算法步骤

  1. 分割:将点集 $P$ 按照 x 坐标排序,并划分为两个子集 $P_L$ 和 $P_R$。
  2. 递归求解:递归地求解子集 $P_L$ 和 $P_R$ 中的最近点对,分别记为 $(p_{L1}, p_{L2})$ 和 $(p_{R1}, p_{R2})$。
  3. 合并:设 $d_L$ 和 $d_R$ 为子集的最小距离。取 $d = \min(d_L, d_R)$。
  4. 创建一个带宽为 $d$ 的矩形带,检查该带内的点,找到跨子集的最近点对。

关键实现

在合并过程中,我们只需要检查带内的点。以下是完整的实现代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
def closest_pair(points):
def distance(p1, p2):
return ((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2) ** 0.5

def find_closest_util(points_sorted_by_x, points_sorted_by_y):
if len(points_sorted_by_x) <= 3:
return naive_closest_pair(points_sorted_by_x)

mid = len(points_sorted_by_x) // 2
midpoint = points_sorted_by_x[mid]

left_y = [p for p in points_sorted_by_y if p[0] <= midpoint[0]]
right_y = [p for p in points_sorted_by_y if p[0] > midpoint[0]]

(p1, p2, left_dist) = find_closest_util(points_sorted_by_x[:mid], left_y)
(p3, p4, right_dist) = find_closest_util(points_sorted_by_x[mid:], right_y)

min_dist = min(left_dist, right_dist)
closest_pair = (p1, p2) if left_dist < right_dist else (p3, p4)

in_strip = [p for p in points_sorted_by_y if abs(p[0] - midpoint[0]) < min_dist]

for i in range(len(in_strip)):
for j in range(i + 1, len(in_strip)):
if (in_strip[j][1] - in_strip[i][1]) < min_dist:
d = distance(in_strip[i], in_strip[j])
if d < min_dist:
min_dist = d
closest_pair = (in_strip[i], in_strip[j])
else:
break

return closest_pair[0], closest_pair[1], min_dist

points_sorted_by_x = sorted(points, key=lambda x: x[0])
points_sorted_by_y = sorted(points, key=lambda x: x[1])

return find_closest_util(points_sorted_by_x, points_sorted_by_y)

# 使用示例
points = [(0, 0), (1, 1), (2, 2), (3, 1), (1, 3)]
closest_pair_result = closest_pair(points)
print("最近点对:", closest_pair_result[:2], "距离:", closest_pair_result[2])

时间复杂度

使用分治法的时间复杂度为 $O(n \log n)$,这是因为在分割阶段需要 $O(n \log n)$ 的时间,而在合并阶段最多需要 $O(n)$ 的时间。这使得该算法在大型数据集处理时远远优于朴素方法。

应用案例

最近点对问题在许多实际应用中都有广泛应用:

  • 图像处理:可以用于特征点匹配。
  • 点云处理:在3D建模中帮助寻找最近邻点,优化模型。
  • 电子商务:基于用户行为数据评估用户之间的相似性和相距问题,以提高推荐系统性能。

以上是关于最近点对问题的详细解析及实现,接下来我们将探讨与此相关的凸包算法,进一步扩展对于空间中点集的处理策略。

9 几何算法之最近点对问题

https://zglg.work/computing-geometry-zero/9/

作者

IT教程网(郭震)

发布于

2024-08-11

更新于

2024-08-12

许可协议

分享转发

交流

更多教程加公众号

更多教程加公众号

加入星球获取PDF

加入星球获取PDF

打卡评论