9 几何算法之最近点对问题
系列进度
计算几何入门 · 第 9 / 18 篇
整理说明
这篇内容怎么整理
郭震 · 2026-06-04
阅读路线
先按这条路线读
先抓住主线,再回到代码、配置和图文细节,读起来会更稳。
计算几何适合用图来理解,关键是把几何对象、关系判断和算法边界放在一起看。阅读时可以按「问题定义 -> 朴素方法 -> 分治法 -> 思路」建立结构,再回到正文里的代码、案例或指标做验证。
读完后,用一个真实小任务复查:输入是什么,处理环节在哪里,输出是否可验收;失败时先查「问题定义」,再查「朴素方法」。
在本章中,我们将探讨计算几何中的一个经典问题:最近点对问题。该问题旨在找到平面或空间中距离最近的一对点。它在诸多应用中具有重要意义,如碰撞检测、路径规划及数据压缩等。
问题定义
给定一个点集 ,每个点 在平面中的坐标为 ,我们需要找到两个点 和 ,使得它们之间的距离最小,即求解以下最小值:
学习最近点对问题时,先看暴力比较的成本,再看按坐标排序、分治合并和候选带筛选如何减少计算。
朴素方法
最简单的方法是使用一个双重循环来检查所有点对的距离,时间复杂度为 。具体实现如下:
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
然而,该算法对于大规模数据集效率极低,因此我们需要更高效的解决方案。
分治法
思路
《几何算法之最近点对问题》适合边看图边读正文。先确认问题和判断标准,再看概念解释与练习步骤,信息会更容易连成一条线。
我们可以利用分治法来减少时间复杂度。该方法的基本思路是将点集分为两半,然后递归地找到每一半中的最近点对。同时,我们还需考虑可能跨越两个子集的最近点对。
算法步骤
- 分割:将点集 按照 x 坐标排序,并划分为两个子集 和 。
- 递归求解:递归地求解子集 和 中的最近点对,分别记为 和 。
- 合并:设 和 为子集的最小距离。取 。
- 创建一个带宽为 的矩形带,检查该带内的点,找到跨子集的最近点对。
关键实现
在合并过程中,我们只需要检查带内的点。以下是完整的实现代码:
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])
时间复杂度
使用分治法的时间复杂度为 ,这是因为在分割阶段需要 的时间,而在合并阶段最多需要 的时间。这使得该算法在大型数据集处理时远远优于朴素方法。
应用案例
最近点对问题在许多实际应用中都有广泛应用:
练习《几何算法之最近点对问题》时,建议把输入条件、处理动作和可见结果写在一起,方便下次复查。
复习《几何算法之最近点对问题》时,建议把关键概念、操作步骤和可见结果放在同一页里回看。
- 图像处理:可以用于特征点匹配。
- 点云处理:在3D建模中帮助寻找最近邻点,优化模型。
- 电子商务:基于用户行为数据评估用户之间的相似性和相距问题,以提高推荐系统性能。
以上是关于最近点对问题的详细解析及实现,接下来我们将探讨与此相关的凸包算法,进一步扩展对于空间中点集的处理策略。
继续阅读
从这篇继续找到相关教程
常见问题
读前先确认这三点
几何算法之最近点对问题适合谁读?
这是 计算几何入门 系列第 9 / 18 篇,适合正在学习计算几何入门,并且需要把概念落到操作步骤或判断标准里的读者。
读这篇计算几何入门教程要多久?
按中文技术文章阅读速度估算,通读大约 3 分钟;如果要跟着复现,建议把命令、配置和结果检查分开做。
这篇文章里的图文节点怎么用?
正文里有 6 个图文节点,可以先用它们抓住流程、配置和判断点,再回到对应段落细读。
分享文章
转发到常用平台
微信/朋友圈可先复制链接
相关教程
从相近问题继续读
继续阅读