Jarvis步进法
Jarvis步进法(Jarvis March),也称为礼品包装算法(Gift Wrapping Algorithm),是一种用于计算二维点集凸包的经典算法。它的核心思想是通过逐步“包裹”点集来找到凸包的边界。对于初学者来说,这是一个直观且易于理解的算法。
什么是凸包?
在开始讲解Jarvis步进法之前,我们需要先了解什么是凸包。凸包是包含给定点集的最小凸多边形。换句话说,它是将所有点“包裹”起来的最小凸形状。凸包在计算几何中有着广泛的应用,例如碰撞检测、路径规划和图像处理等。
Jarvis步进法的基本思想
Jarvis步进法的核心思想是从点集中选择一个起始点(通常是x坐标最小的点),然后逐步找到下一个凸包顶点,直到回到起始点,形成一个闭合的凸包。
算法的步骤如下:
- 选择起始点:找到点集中x坐标最小的点,作为凸包的起始点。
- 寻找下一个顶点:从当前点出发,找到与当前点形成的向量角度最小的点,作为下一个凸包顶点。
- 重复步骤2:将找到的点作为新的当前点,继续寻找下一个顶点,直到回到起始点。
算法实现
下面是一个用Python实现的Jarvis步进法示例:
python
def cross_product(o, a, b):
return (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0])
def jarvis_march(points):
n = len(points)
if n < 3:
return points
# 找到起始点(x坐标最小的点)
start = min(points, key=lambda p: p[0])
hull = [start]
while True:
current = hull[-1]
next_point = points[0]
for p in points:
if p == current:
continue
cross = cross_product(current, next_point, p)
if cross < 0 or (cross == 0 and distance(current, p) > distance(current, next_point)):
next_point = p
if next_point == start:
break
hull.append(next_point)
return hull
def distance(a, b):
return (a[0] - b[0])**2 + (a[1] - b[1])**2
输入和输出示例
假设我们有以下点集:
python
points = [(0, 3), (2, 2), (1, 1), (2, 1), (3, 0), (0, 0), (3, 3)]
调用 jarvis_march(points)
将返回凸包的顶点:
python
[(0, 0), (3, 0), (3, 3), (0, 3)]
逐步讲解
1. 选择起始点
我们首先选择x坐标最小的点作为起始点。在上面的示例中,起始点是 (0, 0)
。
2. 寻找下一个顶点
从起始点出发,我们遍历所有其他点,找到与当前点形成的向量角度最小的点。这个点就是下一个凸包顶点。
3. 重复步骤2
我们将找到的点作为新的当前点,继续寻找下一个顶点,直到回到起始点。
实际应用场景
Jarvis步进法在以下场景中非常有用:
- 碰撞检测:在游戏开发中,凸包可以用于检测两个物体是否可能发生碰撞。
- 路径规划:在机器人导航中,凸包可以用于确定机器人的安全路径。
- 图像处理:在图像识别中,凸包可以用于提取物体的轮廓。
总结
Jarvis步进法是一种简单直观的凸包算法,适合初学者理解和实现。虽然它的时间复杂度为O(nh),其中n是点集的大小,h是凸包的顶点数,但对于小规模的点集来说,它的性能是可以接受的。
附加资源与练习
- 练习1:尝试用Jarvis步进法计算以下点集的凸包:
[(1, 1), (2, 3), (4, 2), (3, 1), (5, 4)]
。 - 练习2:比较Jarvis步进法与Graham扫描法的性能差异。
- 进一步阅读:了解更多关于凸包算法的内容,例如Graham扫描法和Andrew's monotone chain算法。
提示
如果你对计算几何的其他算法感兴趣,可以继续学习Graham扫描法或Andrew's monotone chain算法,它们在某些情况下比Jarvis步进法更高效。