凸包算法——Graham Scan、Jarvis March(Gift Wrapping)实现

凸包算法的目的是从一个点集计算出最小的凸多边形,包围所有点。

1. Graham Scan

  • 步骤:

    1. 选择基准点: 选择 y 坐标最小的点(如果有多个,选择 x 坐标最小的点)。
    2. 按极角排序: 计算所有点相对于基准点的极角,并按极角排序。
    3. 构建凸包: 使用栈来维护凸包的顶点。逐一检查点,保证凸包是凸的,弹出不符合条件的点,最终栈中的点即为凸包的顶点。
  • 时间复杂度: O(n log n)(排序步骤占主导)

代码:

import numpy as np

def graham_scan(points):
    points = sorted(points,key = lambda p:(p[0],p[1]))
    def orientation(p,q,r):
        return (q[1] - p[1]) * (r[0] - q[0]) - (q[0] - p[0]) * (r[1] -q[1])
    
    def build_hull(points):
        hull = []
        for p in points:
            while len(hull) >= 2 and orientation(hull[-2],hull[-1],p) <= 0:
                hull.pop()
            hull.append(p)
        return hull

    lower = build_hull(points)
    upper = build_hull(reversed(points))

    return np.array(lower[:-1] + upper[:-1])


points = np.array([[-5.90257, -0.22237], [1.03258, -0.533936], [0.907549, -0.134126], 
                   [0.717333, 0.471533], [0.713893, 0.484054], [0.708021, 0.510585], 
                   [0.668892, 0.637087], [0.5562, -0.748886], [0.552133, 1.01539], 
                   [-1.36528, 1.202], [-1.49895, 1.6176], [-1.50235, 1.62983], 
                   [-1.50702, 1.64434], [-1.51095, 1.65558], [-1.51498, 1.66897], 
                   [-1.51726, 1.67879], [-1.5189, 1.68445], [-1.54057, 1.75716], 
                   [-1.5986, -1.40478], [-2.28137, -1.61885], [-2.43108, -1.66877], 
                   [-2.49418, 1.55916], [-4.34462, -2.27342], [-4.35825, 0.963766], 
                   [-4.43426, -2.3003], [-5.8972, -0.250361]])
hull_points = graham_scan(points)

2. Jarvis March (Gift Wrapping)

  • 步骤:

    1. 选择起点: 选择最左边的点作为起点。
    2. 寻找下一个点: 从当前点开始,选择相对于当前点所有其他点中极角最小的点作为下一个凸包点。
    3. 重复直到封闭: 继续选择极角最小的点,直到回到起点形成闭环。
  • 时间复杂度: O(nh)(h 为凸包的顶点数,最坏情况下为 O(n^2))

代码:

import numpy as np

def jarvis_march(points):
    hull = []
    leftmost = np.argmin(points[:, 0])
    point_on_hull = leftmost
    while True:
        hull.append(point_on_hull)
        endpoint = (point_on_hull + 1) % len(points)
        for i in range(len(points)):
            if np.cross(points[endpoint] - points[point_on_hull], points[i] - points[point_on_hull]) < 0:
                endpoint = i
        point_on_hull = endpoint
        if endpoint == leftmost:
            break
    return np.array(hull)

points = np.array([[-5.90257, -0.22237], [1.03258, -0.533936], [0.907549, -0.134126], 
                   [0.717333, 0.471533], [0.713893, 0.484054], [0.708021, 0.510585], 
                   [0.668892, 0.637087], [0.5562, -0.748886], [0.552133, 1.01539], 
                   [-1.36528, 1.202], [-1.49895, 1.6176], [-1.50235, 1.62983], 
                   [-1.50702, 1.64434], [-1.51095, 1.65558], [-1.51498, 1.66897], 
                   [-1.51726, 1.67879], [-1.5189, 1.68445], [-1.54057, 1.75716], 
                   [-1.5986, -1.40478], [-2.28137, -1.61885], [-2.43108, -1.66877], 
                   [-2.49418, 1.55916], [-4.34462, -2.27342], [-4.35825, 0.963766], 
                   [-4.43426, -2.3003], [-5.8972, -0.250361]])

hull_indices = jarvis_march(points)
hull_points = points[hull_indices]

print("Convex Hull points:", hull_points)

  • 7
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
凸包算法是一种计算给定点集的凸包的方法。其中,Graham扫描算法Jarvis步进算法是两种常见且经典的凸包算法。 1. Graham扫描算法: - 首先,选择一个点作为起始点(通常选择最下方的点,如果存在多个最下方的点,则选择最左边的点)。 - 将其余点按照相对于起始点的极角进行排序(逆时针排序)。 - 依次遍历排序后的点集,对每个点进行如下判断: - 如果当前点与栈顶的两个点构成的向量形成逆时针转向,将该点入栈。 - 否则,将栈顶的点出栈,直到当前点与栈顶的两个点构成的向量形成逆时针转向,然后将当前点入栈。 - 遍历结束后,栈中剩余的点即为凸包上的点。 2. Jarvis步进算法(也称为Gift Wrapping算法): - 首先,选择一个起始点(通常选择最左边的点)作为凸包上的一个点。 - 从起始点开始,依次选择能够使得当前点与下一个点构成的向量形成逆时针转向的下一个点,将其加入凸包。 - 重复上述过程,直到再次回到起始点为止。 这两种算法都是基于极角的思想,通过不断地寻找相邻点之间的逆时针转向来构建凸包。它们的时间复杂度都是O(nh),其中n是点的个数,h是凸包上的点的个数。相较而言,Graham扫描算法在一般情况下更快一些,但在特殊情况下可能会出现退化,而Jarvis步进算法则相对稳定但效率较低。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值