convex combination: 凸组合(系数和为1)
convex set: 凸集(点集) 任一子集的凸组合仍是子集
convex hull: 凸闭包[conv§] 凸集的所有子集的所有凸组合
CH§: the boundary of conv§
extreme points, EP
定义:存在一条线L穿过点P,凸集中其他所有点在L的同一侧
算法:查找凸集中所有extreme points
思路:为遍历判断,EP不在任一三角形内, O(n^4)
extreme edge, EE:
定义:CH§的所有边
算法:通过找EE找出所有的EP
思路:
(1)逐条边检查,所有点都在EE同一侧,O(n^3)
(2)借鉴插入排序,减而治之,从小点集出发逐个加点,加入时判断是否在CH§中,在则加入点集并更新其他点,否则跳过。判断是否在CH§的方法:toLeft(x, v, v.pre) + toLeft(x, v, v.next)。逐个加点O(n),判断O(n),复杂度O(n^2)
(3)借助选择排序,从lowest-then-leftmost EP开始,逐个加入满足minimal left-turn的EP(即加入next EE),判断mlt的方法是遍历点集每次找位于当前边右边的点。总共加h(EE数量)次,每次判断n个点,复杂度O(nh)
python实现:
class Point:
def __init__(self, x, y, extreme=False) -> None:
self.extreme = extreme
self.x = x
self.y = y
"""
Algorithm of finding extreme points
"""
def extremePoints(S: list):
"""Compute extreme points of convex hull S
Args
S: set of points, len(S)>2
"""
for p in S:
p.extreme = True
n = len(S)
for p in range(n):
for q in range(p + 1, n):
for r in range(q + 1, n):
for s in range(r + 1, n):
if s == p or s == q or s == r or S[s].extreme == False:
continue
if inTriangle(S[p], S[q], S[r], S[s]):
S[s].extreme = False
def inTriangle(p: Point, q: Point, r:Point, s: Point) -> bool:
"""Judge weather s in triangle composed of (p, q, r)
"""
pql = toLeft(p, q, s)
qrl = toLeft(q, r, s)
rpl = toLeft(r, p, s)
return pql == qrl and qrl == rpl
def toLeft(a: Point, b: Point, s:Point) -> bool:
"""Judge weather s in the left of vector ab
"""
return area2(a, b, s) > 0
def area2(a:Point, b:Point, s:Point) -> float:
"""Compute area * 2 of triangle composed of (a, b, s)
"""
return b.x * s.y - b.y * s.x + \
a.y * s.x - a.x * s.y + \
a.x * b.y - a.y * b.x
"""
Algorithm of finding extreme edges
"""
def ltl(points: list) -> Point:
"""Find lowest-then-leftmost point of set points
"""
ans = points[0]
for p in points:
if p.y < ans.y or p.y == ans.y and p.x < ans.x:
ans = p
return ans
def extremePointsByEdges(points: list):
"""Find all extreme points by finding extreme edges, O(nh)
"""
for p in points: p.extreme = False
k = LTL = ltl(points)
while True:
k.extreme = True
tmp = None
for p in points:
if p == k: continue
if not tmp or not toLeft(k, tmp, p):
tmp = p
k.succ = tmp
k = tmp
if k == LTL:
break