zoukankan      html  css  js  c++  java
  • 凸包

    凸包

    参考:博客

    凸包问题是指在n个点中,寻找一个凸多边形,使所有的点在凸多边形的边界或者内部。

    首先,x轴最大最小点都在凸包边界上,y轴最大最小点都在凸包边界上。面试官提醒可以使用扫描法,就是假设使用一根绳子绕逆时针方向旋转,被绕到的都在凸包边界上。

    思路简单清晰,但是在具体实现方面遇到了困难。
    问题:

    • 怎么模拟绕的过程
      • 如何找到绕线的起始点;
      • 即如何找到绕线的时候碰到的第一个点;
      • 怎么判断这个点在不在凸包里面;

    方法:

    • 第一个点可以选择最大最小点,这个相对简单;
    • 绕线的时候,线与x轴的夹角是从小到大变化的;所以当新加入一个点的时候,需要计算这个点到所有点的极角并排序;
    • 可以使用三个点构成的两条线段的叉乘来判断;

    极角排序
    用来确定点的处理顺序

    叉乘判断三角形方向
    利用矢量叉积判断是逆时针还是顺时针,参考 博客

    设A(x1,y1),B(x2,y2),C(x3,y3),则三角形两边的矢量分别是:
    AB=(x2-x1,y2-y1), AC=(x3-x1,y3-y1)
    则AB和AC的叉积为:(2*2的行列式)
    |x2-x1, y2-y1|
    |x3-x1, y3-y1|
    值为:((x2-x1)*(y3-y1) - (y2-y1)*(x3-x1))

    利用右手法则进行判断:

    • 如果AB*AC>0,则三角形ABC是逆时针的
    • 如果AB*AC<0,则三角形ABC是顺时针的
    • 如果……  =0,则说明三点共线,

    伪代码:

    computational graph

    参考两位大神的博客:

    import math
    
    def get_leftbottom_point(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'])
    
    def cal_arc(p1,p0):
        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(p,k):
        p2 = []
        for i in range(len(p)):
            p2.append({"indx":i,"arc":cal_arc(p[i],p[k])})
        p2.sort(key=lambda x: x.get("arc",0))
        p_out = []
        for i in range(len(p2)):
            p_out.append(p[p2[i]["indx"]])
        return p_out
    
    def graham_scan(p):
        k = get_leftbottom_point(p)
        p_sort = sort_points(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
    
    if __name__=="__main__":
        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":1.5,"y":1.5}]
        print(graham_scan(ps))
    
  • 相关阅读:
    Atitit.ide eclipse编译速度慢的解决
    Atitit.ide eclipse编译速度慢的解决
    Atitit.可视化与报表原理与概论
    Atitit.可视化与报表原理与概论
    Atitit.数据检索与网络爬虫与数据采集的原理概论
    Atitit.数据检索与网络爬虫与数据采集的原理概论
    Atitit 游戏的原理与概论attilax总结
    Atitit 游戏的原理与概论attilax总结
    Atiti.ui原理与gui理论
    Atiti.ui原理与gui理论
  • 原文地址:https://www.cnblogs.com/curtisxiao/p/11195155.html
Copyright © 2011-2022 走看看