📅  最后修改于: 2023-12-03 15:35:52.442000             🧑  作者: Mango
凸包(Convex Hull)是在平面(x-y平面)中,包含一组点的最小凸多边形的边缘。给定一个点集,求出这个点集的凸包。对于凸包的周长,是指凸包上所有边的长度之和。
目前凸包的求解有两种经典算法:Graham算法和Jarvis算法。由于Graham算法效率高,实际应用中最广泛。
Graham算法的核心思想:以与x轴正方向夹角从小到大排序的方式,对点进行排序;扫描每个点,根据“右转”判断来决定是否放入凸包中。
Graham算法的时间复杂度为O(nlogn)。
我们可以先给出一个最简单的求凸包周长的代码:
import math
def dist(p1, p2):
"""计算两点间距离"""
return math.sqrt((p2[0] - p1[0]) ** 2 + (p2[1] - p1[1]) ** 2)
def convex_hull(points):
"""求凸包"""
points = sorted(set(points)) # 排序并去重
if len(points) <= 1:
return points
# 从左下角和右上角开始
lower = []
for p in points:
while len(lower) >= 2 and (lower[-1][0] - lower[-2][0]) * (p[1] - lower[-2][1]) - \
(lower[-1][1] - lower[-2][1]) * (p[0] - lower[-2][0]) >= 0:
lower.pop()
lower.append(p)
upper = []
for p in reversed(points):
while len(upper) >= 2 and (upper[-1][0] - upper[-2][0]) * (p[1] - upper[-2][1]) - \
(upper[-1][1] - upper[-2][1]) * (p[0] - upper[-2][0]) >= 0:
upper.pop()
upper.append(p)
return lower[:-1] + upper[:-1][::-1]
def perimeter(points):
"""计算周长"""
conv = convex_hull(points)
if len(conv) <= 1:
return 0
p = conv[0]
s = 0
for q in conv[1:]:
s += dist(p, q)
p = q
return s + dist(conv[-1], conv[0])
接下来,我们对上述代码进行 markdown 格式解释:
def dist(p1, p2):
"""计算两点间距离"""
return math.sqrt((p2[0] - p1[0]) ** 2 + (p2[1] - p1[1]) ** 2)
两点间距离可使用勾股定理计算得出,代码实现如上。
def convex_hull(points):
"""求凸包"""
points = sorted(set(points)) # 排序并去重
if len(points) <= 1:
return points
# 从左下角和右上角开始
lower = []
for p in points:
while len(lower) >= 2 and (lower[-1][0] - lower[-2][0]) * (p[1] - lower[-2][1]) - \
(lower[-1][1] - lower[-2][1]) * (p[0] - lower[-2][0]) >= 0:
lower.pop()
lower.append(p)
upper = []
for p in reversed(points):
while len(upper) >= 2 and (upper[-1][0] - upper[-2][0]) * (p[1] - upper[-2][1]) - \
(upper[-1][1] - upper[-2][1]) * (p[0] - upper[-2][0]) >= 0:
upper.pop()
upper.append(p)
return lower[:-1] + upper[:-1][::-1]
这部分代码使用 Graham 算法求解凸包。详细的实现过程请参考其他文献或资料。
def perimeter(points):
"""计算周长"""
conv = convex_hull(points)
if len(conv) <= 1:
return 0
p = conv[0]
s = 0
for q in conv[1:]:
s += dist(p, q)
p = q
return s + dist(conv[-1], conv[0])
根据求得的凸包,遍历凸包上的所有点并计算两点间距离之和,最后得出的结果即为凸包的周长。