凸包实现Graham扫描法, Python

本文介绍了如何使用格拉姆扫描算法来找出多边形中的凸包,通过计算极角排序并迭代检查点的连接性来确定基准点和最终凸包。算法的时间复杂度为O(nlgn),适合处理二维点集。

目录

算法分三个部分:

代码


算法分三个部分:


1、寻找y轴最小的点,如果y轴位置是相同的,那个找x轴位置最小的,称之为基准点。
2、计算1中找到基准点与其他点的极角(即过此2点的直线与x轴正方向的夹角,代码中以弧度表示),将这些点按极角的大小正序排列。
3、进行基准点与2中点的连线迭代,对新连线的点计算其是否符合凸多边形的定义,如果不满足舍弃此点。判断的方法是计算三点组成线段的叉乘,值为正表示满足条件。


运行时间为O(nlgn)

代码

import math
import matplotlib.pyplot as plt
#待测试点集
c = [2, 1, 2, 1.5, 1, 3, 1.5, 0.5, 1.5, 3]
b = [2, 1, 1, 1.5, 2, 1.5, 1.2, 2, 0.5, 0.5]
ps = [{"x": 2, "y": 2}, {"x": 1, "y": 1}, {"x": 2, "y": 1}, {"x": 1.5, "y": 1.5}, {"x": 1, "y": 2}, {"x": 3, "y": 1.5},
      {"x": 1.5, "y": 1.2}, {"x": 0.5, "y": 2}, {"x": 1.5, "y": 0.5}, {"x": 3, "y": 0.5}]

#获取基准点的下标
def get_leftbottompoint(p):
    k = 0
    for i in range(1, len(p)):
        if p[i]['y'] < p[k]['y'] or (p[i]['y'] == p[k]['y'] and p[i]['x'] < p[k]['x']):
            k = i
    return k

#叉乘计算方法
def multiply(p1, p2, p0):
    return (p1['x'] - p0['x']) * (p2['y'] - p0['y']) - (p2['x'] - p0['x']) * (p1['y'] - p0['y'])

#获取极角,通过求反正切得出,考虑pi / 2的情况
def get_arc(p1, p0):
    # 兼容sort_points_tan的考虑
    if (p1['x'] - p0['x']) == 0:

        if ((p1['y'] - p0['y'])) == 0:
            return -1;
        else:
            return math.pi / 2

    tan = float((p1['y'] - p0['y'])) / float((p1['x'] - p0['x']))
    arc = math.atan(tan)
    if arc >= 0:
        return arc
    else:
        return math.pi + arc

#对极角进行排序
def sort_points_tan(p, k):
    p2 = []
    for i in range(0, len(p)):
        p2.append({"index": i, "arc": get_arc(p[i], p[k])})
    p2.sort(key=lambda k: (k.get('arc', 0)))
    p_out = []
    for i in range(0, len(p2)):
        p_out.append(p[p2[i]["index"]])
    return p_out


def graham_scan(p):
    k = get_leftbottompoint(p)
    p_sort = sort_points_tan(p, k)

    p_result = [None] * len(p_sort)
    p_result[0] = p_sort[0]
    p_result[1] = p_sort[1]
    p_result[2] = p_sort[2]

    top = 2
    for i in range(3, len(p_sort)):
        #叉乘为正则符合条件
        while (top >= 1 and multiply(p_sort[i], p_result[top], p_result[top - 1]) > 0):
            top -= 1
        top += 1
        p_result[top] = p_sort[i]

    for i in range(len(p_result) - 1, -1, -1):
        if p_result[i] == None:
            p_result.pop()

    return p_result
#测试
result = graham_scan(ps)
print (result)
plt.plot(c, b, 'ro')
c=[]
b=[]
for i in range(0, len(result)):
    c.append(result[i]['x'])
    b.append(result[i]['y'])
c.append(result[0]['x'])
b.append(result[0]['y'])
plt.plot(c, b, 'r-')
plt.axis([0, 4, 0, 4])
plt.show()

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Alocus_

如果我的内容帮助到你,打赏我吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值