Graham算法构造凸包(python)

一、算法步骤

  1. 首先找到点集中y坐标最小的点作为初始点 p 0 p_0 p0,若y坐标相同,选取x坐标最小的一个点作为 p 0 p_0 p0
  2. 以p0为原点,对点集进行极角排序得到集合 { p 0 , p 1 . . . p n } \left\{p_0,p_1...p_n\right\} {p0,p1...pn}极角排序后的点集
  3. 令i=1,构建一个存储凸包边界的栈stack,栈内初始存储元素 p 0 p_0 p0 p 1 p_1 p1
  4. 判断集合下一个元素 p i + 1 p_{i+1} pi+1在栈顶两元素构成的向量的哪一侧:
       若在左侧,则点 p i + 1 p_{i+1} pi+1进栈, i = i + 1 ; i=i+1; i=i+1;重复执行步骤3,直至 i > n i>n i>n
       若在右侧,则栈顶元素出栈,重复执行步骤3

二、算法示意图

三、代码

1.导入python库

from math import atan2
import math
import matplotlib.pyplot as plt
import numpy as np

2.构造工具函数

def atan(point,y,x):
    '''返回点(x,y)相对于point的角度'''
    x = x - point[0]
    y = y - point[1]
    if x==0 and y == 0:
        return 0
    point = (5,0) #表示x轴的向量,随便取
    cos = (point[0]*x+point[1]*y)/(math.sqrt(point[0]**2+point[1]**2)*math.sqrt(x**2+y**2))
    return np.arccos(cos)*(180/math.pi)
def angle_sort(p0,points):
    '''对点集从小到大进行极角排序'''
    dic = {}
    for point in points:
        angle = atan(p0,point[1],point[0])
        dic[point] = angle
    points = [k[0] for k in sorted(dic.items(),key=lambda x:x[1])] #,reverse=True
    return points
def cross_product(a,b,c):
    '''判断点c在由点a,b构成的向量的那一侧'''
    result = a[0]*b[1]-a[1]*b[0] + b[0]*c[1]-b[1]*c[0] + c[0]*a[1]-c[1]*a[0]
    if result < 0:
        return False #点c在向量ab右边 返回False
    else:
        return True #点c在向量ab左边  返回True

3.初始化点集

集合的高度由vertical决定,宽度由level决定

key=3
num=120
level = 10
vertical = 5
seed = np.random.RandomState(key)
seed2 = np.random.RandomState(key+1)
Z1 = seed.rand(num,1)*level #生成点集
Z2 = seed2.rand(num,1)*vertical
Z = np.concatenate([Z1,Z2],axis = 1)
points = [tuple(i) for i in Z]

三、计算凸包

#起点为y坐标最小的点
ymin = min(points,key=lambda x:x[0])[1]
start = min([i for i in points if i[1] == ymin],key = lambda x:x[0])
stack=[] #定义栈
points = angle_sort(start,points)
stack.append(points[0])
stack.append(points[1])
i=2
while len(stack)!=0 and i != len(points):
    if cross_product(stack[len(stack)-2],stack[len(stack)-1],points[i]):
        stack.append(points[i])
        i+=1
    else:
        stack.pop()
        if len(stack)<2:
            stack.append(points[i])
            i+=1
        continue

四、画出凸包

stack.append(stack[0])
x = [i[0] for i in points]
y = [i[1] for i in points]
x0 = [i[0] for i in stack]
y0 = [i[1] for i in stack]
plt.figure(figsize=(10,10))
plt.scatter(x,y)
plt.plot(x0,y0)

plt.xlim(-1,11)
plt.ylim(-1,6)
i=0
for a,b in zip(x,y):
    plt.text(a, b, i)
    i+=1

在这里插入图片描述

该算法参考自算法导论第33章凸包的生成

  • 1
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值