正好用到求凸包(Convex Polygon)的问题,就把他记下来

本文介绍了凸多边形的概念及其定义,并通过实例解释了如何判断一个多边形是否为凸多边形。此外,还详细阐述了凸包的概念,即给定点集的最小面积凸多边形。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


在了解凸包之前,須先認識何謂「凸多邊形」(Convex Polygon)。從直觀上說,一個凸多邊形就是沒有任何凹陷位的多邊形。我們在低年級數學所學習的三角形、正方形、長方形、平行四邊形、正五邊形、正六邊形等等,都是凸多邊形的例子。但是以下這個「凸」字形卻並非凸多邊形,因為箭頭指著的地方實際是一個凹陷位。

可是上述這一定義很不嚴密,究竟何謂「凹陷位」?實在難以說清楚。因此在數學上,凸多邊形有另一個嚴格的定義。假設我們在一個多邊形上(包括多邊形的邊界及邊界圍封的範圍)任意取兩點並以一條線段連結該兩點,如果線段上的每一點均在該多邊形上,那麼我們便說這個多邊形是凸的。根據以上定義,我們便可判斷「凸」字形的確不是凸的。例如,在下圖中,連結A、B兩點的線段有一部分並不在該多邊形上。

認識了凸多邊形後,我們便可了解何謂凸包。給定平面上的一個(有限)點集(即一組點),這個點集的凸包就是包含點集中所有點的最小面積的凸多邊形。例如,下圖的點集共包含9個點,圖中的六邊形便是該點集的凸包。其中構成六邊形的6個點稱為「凸包上的點」(Hull Point),其餘3個點則並非「凸包上的點」。請注意上述定義中「最小面積」這個限制條件,因為除了凸包以外,還有無限多個包含點集中所有點的凸多邊形。例如,只要畫一個面積足夠大的四邊形,便可包圍任意給定的點集。因此假如沒有這個限制條件,求凸包就變成非常容易但卻沒有唯一解的運算。

### Python 实现多边形凸包计算 以下是基于 **Graham Scan** 和 **Jarvis March (Gift Wrapping)** 的两种方法实现多边形凸包的 Python 示例代码。 #### 方法一:Graham Scan 算法 Graham Scan 是一种经典的凸包算法,其核心思想是通过极角排序和栈操作来构建凸包。以下是其实现: ```python def cross_product(p1, p2, p3): """计算向量叉积""" return (p2[0] - p1[0]) * (p3[1] - p1[1]) - (p2[1] - p1[1]) * (p3[0] - p1[0]) def polar_angle(base_point, point): """计算点相对于基准点的极角""" from math import atan2 return atan2(point[1] - base_point[1], point[0] - base_point[0]) def graham_scan(points): """Graham Scan 算法实现""" if len(points) < 3: return points # 找到最左下角的点作为基准点 start = min(points, key=lambda p: (p[1], p[0])) # 排序其他点按照与基准点的极角大小 sorted_points = sorted(points, key=lambda p: (polar_angle(start, p), (p[0] - start[0])**2 + (p[1] - start[1])**2)) hull = [] for point in sorted_points: while len(hull) >= 2 and cross_product(hull[-2], hull[-1], point) <= 0: hull.pop() hull.append(point) return hull[:-1] # 测试数据 points = [(0, 0), (1, 1), (2, 2), (3, 3), (4, 0), (3, -1), (2, -2)] convex_hull = graham_scan(points) print("Convex Hull using Graham Scan:", convex_hull) ``` 此代码实现了 Graham Scan 算法的核心逻辑[^1],并返回输入点集的凸包顶点列表。 --- #### 方法二:Jarvis March (Gift Wrapping) 算法 Jarvis March 又称为 Gift Wrapping Algorithm,它是一种简单直观的凸包算法。以下是其实现: ```python def jarvis_march(points): """Jarvis March 算法实现""" if len(points) < 3: return points # 寻找最左侧最低点作为起始点 start = min(points, key=lambda p: (p[1], p[0])) hull = [start] current = start while True: next_point = points[0] for candidate in points[1:]: if candidate == current: continue turn = cross_product(current, next_point, candidate) if turn > 0 or (turn == 0 and distance_sq(candidate, current) > distance_sq(next_point, current)): next_point = candidate if next_point == start: break hull.append(next_point) current = next_point return hull def distance_sq(p1, p2): """计算两点之间的平方距离""" return (p1[0] - p2[0])**2 + (p1[1] - p2[1])**2 # 测试数据 points = [(0, 0), (1, 1), (2, 2), (3, 3), (4, 0), (3, -1), (2, -2)] convex_hull = jarvis_march(points) print("Convex Hull using Jarvis March:", convex_hull) ``` 该代码展示了 Jarvis March 算法的工作原理[^4],并通过逐步选择下一个顶点的方式构建凸包。 --- ### 多边形面积计算 在获得凸包之后,可以进一步计算多边形的面积。以下是一个简单的函数用于计算多边形面积[^2]: ```python def polygon_area(vertices): """计算多边形面积""" area = 0 n = len(vertices) for i in range(n): j = (i + 1) % n area += vertices[i][0] * vertices[j][1] area -= vertices[j][0] * vertices[i][1] return abs(area) / 2 # 计算凸包面积 area = polygon_area(convex_hull) print("Area of Convex Hull:", area) ``` 上述代码利用了鞋带公式(Shoelace Formula),能够高效地计算任意简单多边形的面积。 --- ### 性能比较 - **Graham Scan**: 时间复杂度为 \( O(n \log n) \),适用于大多数场景。 - **Jarvis March**: 时间复杂度为 \( O(nh) \),其中 \( h \) 表示凸包上的顶点数量。当 \( h \) 较小时性能较好,但在最坏情况下可能退化至 \( O(n^2) \)。 因此,在实际应用中可以根据具体需选择合适的算法。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值