Python 使用 Graham 扫描的凸包(Convex Hull using Graham Scan)
先决条件:
如何检查两个给定的线段是否相交?
c++ https://blog.csdn.net/hefeng_aspnet/article/details/141713655
java https://blog.csdn.net/hefeng_aspnet/article/details/141713762
python https://blog.csdn.net/hefeng_aspnet/article/details/141714389
C# https://blog.csdn.net/hefeng_aspnet/article/details/141714420
Javascript https://blog.csdn.net/hefeng_aspnet/article/details/141714442
贾维斯算法
c++ https://blog.csdn.net/hefeng_aspnet/article/details/141716082
java https://blog.csdn.net/hefeng_aspnet/article/details/141716363
python https://blog.csdn.net/hefeng_aspnet/article/details/141716444
C# https://blog.csdn.net/hefeng_aspnet/article/details/141716403
Javascript https://blog.csdn.net/hefeng_aspnet/article/details/141716421
凸包是包含给定点集的最小凸多边形。它是计算几何学中一个有用的概念,在计算机图形学、图像处理和碰撞检测等各个领域都有应用。
凸多边形是所有内角都小于180度的多边形。可以为任意点集构造凸包,无论这些点如何排列。
格雷厄姆扫描算法:
格雷厄姆扫描算法是一种简单而有效的算法,用于计算一组点的凸包。它的工作原理是迭代地将点添加到凸包中,直到所有点都添加完毕。
该算法首先找到 y 坐标最小的点。该点始终位于凸包上。然后,该算法根据剩余点相对于起点的极角对它们进行排序。
然后,算法会迭代地将点添加到凸包中。在每个步骤中,算法都会检查添加到凸包中的最后两个点是否形成右转。如果是,则将最后一个点从凸包中移除。否则,将排序列表中的下一个点添加到凸包中。
当所有点都被添加到凸包时,算法终止。
Graham Scan的实现:
第一阶段(排序点):
实现格雷厄姆扫描算法的第一步是按照点相对于起点的极角对点进行排序。
一旦点排序完毕,起点就会添加到凸包中。一旦点排序完毕,它们就会形成一条简单的闭合路径(见下图)。
第二阶段(接受或拒绝点):
一旦我们有了闭合路径,下一步就是遍历该路径并移除该路径上的凹点。如何决定移除哪个点以及保留哪个点?同样,方向在这里有帮助。排序数组中的前两个点始终是凸包的一部分。对于剩余的点,我们跟踪最近的三个点,并找到它们形成的角度。让这三个点分别为prev(p)、curr(c)和next(n)。如果这些点的方向(按相同顺序考虑它们)不是逆时针的,我们丢弃 c,否则我们保留它。下图显示了此阶段的分步过程。
下面是上述算法的实现:
# A Python3 program to find convex hull of a set of points. Refer
//c++ C++ 3 个有序点的方向(Orientation of 3 ordered points)-CSDN博客
//java Java 3 个有序点的方向(Orientation of 3 ordered points)-CSDN博客
//python Python 3 个有序点的方向(Orientation of 3 ordered points)-CSDN博客
//C# C# 3 个有序点的方向(Orientation of 3 ordered points)-CSDN博客
//Javascript javascript 3 个有序点的方向(Orientation of 3 ordered points)-CSDN博客
# for explanation of orientation()
from functools import cmp_to_key
# A class used to store the x and y coordinates of points
class Point:
def __init__(self, x = None, y = None):
self.x = x
self.y = y
# A global point needed for sorting points with reference
# to the first point
p0 = Point(0, 0)
# A utility function to find next to top in a stack
def nextToTop(S):
return S[-2]
# A utility function to return square of distance
# between p1 and p2
def distSq(p1, p2):
return ((p1.x - p2.x) * (p1.x - p2.x) +
(p1.y - p2.y) * (p1.y - p2.y))
# To find orientation of ordered triplet (p, q, r).
# The function returns following values
# 0 --> p, q and r are collinear
# 1 --> Clockwise
# 2 --> Counterclockwise
def orientation(p, q, r):
val = ((q.y - p.y) * (r.x - q.x) -
(q.x - p.x) * (r.y - q.y))
if val == 0:
return 0 # collinear
elif val > 0:
return 1 # clock wise
else:
return 2 # counterclock wise
# A function used by cmp_to_key function to sort an array of
# points with respect to the first point
def compare(p1, p2):
# Find orientation
o = orientation(p0, p1, p2)
if o == 0:
if distSq(p0, p2) >= distSq(p0, p1):
return -1
else:
return 1
else:
if o == 2:
return -1
else:
return 1
# Prints convex hull of a set of n points.
def convexHull(points, n):
# Find the bottommost point
ymin = points[0].y
min = 0
for i in range(1, n):
y = points[i].y
# Pick the bottom-most or choose the left
# most point in case of tie
if ((y < ymin) or
(ymin == y and points[i].x < points[min].x)):
ymin = points[i].y
min = i
# Place the bottom-most point at first position
points[0], points[min] = points[min], points[0]
# Sort n-1 points with respect to the first point.
# A point p1 comes before p2 in sorted output if p2
# has larger polar angle (in counterclockwise
# direction) than p1
p0 = points[0]
points = sorted(points, key=cmp_to_key(compare))
# If two or more points make same angle with p0,
# Remove all but the one that is farthest from p0
# Remember that, in above sorting, our criteria was
# to keep the farthest point at the end when more than
# one points have same angle.
m = 1 # Initialize size of modified array
for i in range(1, n):
# Keep removing i while angle of i and i+1 is same
# with respect to p0
while ((i < n - 1) and
(orientation(p0, points[i], points[i + 1]) == 0)):
i += 1
points[m] = points[i]
m += 1 # Update size of modified array
# If modified array of points has less than 3 points,
# convex hull is not possible
if m < 3:
return
# Create an empty stack and push first three points
# to it.
S = []
S.append(points[0])
S.append(points[1])
S.append(points[2])
# Process remaining n-3 points
for i in range(3, m):
# Keep removing top while the angle formed by
# points next-to-top, top, and points[i] makes
# a non-left turn
while ((len(S) > 1) and
(orientation(nextToTop(S), S[-1], points[i]) != 2)):
S.pop()
S.append(points[i])
# Now stack has the output points,
# print contents of stack
while S:
p = S[-1]
print("(" + str(p.x) + ", " + str(p.y) + ")")
S.pop()
# Driver Code
input_points = [(0, 3), (1, 1), (2, 2), (4, 4),
(0, 0), (1, 2), (3, 1), (3, 3)]
points = []
for point in input_points:
points.append(Point(point[0], point[1]))
n = len(points)
convexHull(points, n)
# This code is contributed by Kevin Joshi
输出:
(0,3)
(4,4)
(3,1)
(0,0)
时间复杂度: O(nLogn),其中 n 为输入点的数量。
第一步(找到最底部的点)需要 O(n) 时间。第二步(对点进行排序)需要 O(nLogn) 时间。第三步需要 O(n) 时间。在第三步中,每个元素最多被推送和弹出一次。因此,第六步逐个处理点需要 O(n) 时间,假设堆栈操作需要 O(1) 时间。总体复杂度为 O(n) + O(nLogn) + O(n) + O(n),即 O(nLogn)。
辅助空间: O(n),因为使用了显式堆栈,所以没有占用额外的空间。
凸包的应用:
凸包的应用范围很广,包括:
碰撞检测:凸包可用于快速确定两个物体是否发生碰撞。这在计算机图形学和物理模拟中很有用。
图像处理:凸包可用于分割图像中的对象。这对于诸如对象识别和跟踪之类的任务很有用。
计算几何:凸包用于各种计算几何算法,例如查找最近的点对和计算一组点的直径。