凸包问题的蛮力算法及python实现

凸包问题的蛮力算法及python实现


蛮力法的基本思想是先用排除法确定凸包的顶点,然后按逆时针顺序输出这些顶点。在判断点P是不是凸包上的顶点时,有如下性质:

给定平面点集S,P,Pi,Pj,Pk是S中四个不同的点,如果P位于Pi,Pj,Pk组成的三角形内部或边界上,则P不是S的凸包顶点。

那么如何判断点P在三角形内部或边界上?给定平面两点AB,直线方程g(A,B,P)=0时,P位于直线上,g(A,B,P)>0和g(A,B,P)<0时,P分别位于直线的两侧。

判断点P在三角形内部或边界上只需依次检查P和三角形的每个顶点是否位于三角形另外两个顶点确定的直线的同一侧,即判断:
t1=g(pj,pk,p)*g(pj,pk,pi)>=0 ,
t2=g(pi,pk,p)*g(pi,pk,pj)>=0,
t3=g(pj,pi,p)*g(pj,pi,pk)>=0
是否同时成立


凸包问题的蛮力算法伪代码如下:

BruteForce(S):
输入:平面n个点的集合S
输出:按逆时针顺序输出S的凸包的所有顶点
If n=3 
   Then 以逆时针顺序输出S的顶点,算法结束
找到S中纵坐标最小的点P,该点一定位于凸包上
For S中任意三点Pi,Pj,Pk Do
   If Pi,Pj,Pk 一点位于其他两点与P构成的三角形内 
      Then 删除该点
找出S中横坐标最小的点A和横坐标最小的点B
将S划分问直线AB上方点集SU,直线AB下方点集SL,A,B两点属于SL
将SL按横坐标递增排序,SU按横坐标递减排序
顺序输出SL,SU

首先随机生成点集S


import random
import itertools

def generate_num(n):
     random_list = list(itertools.product(range(1, 100), range(1, 100)))
     lists=random.sample(random_list, n)
     return  lists

  • 判断点P在三角形内部或边界上,即判断点P是否在凸包上
  • 在具体的判断过程中,尤其时坐标点比较密集的情况下,还有有三种比较特殊的情况
  • 组成直线的两点垂直于x轴
  • 除点P外其余三点在一条直线上时,不应删除点P,因为此时点P可能时凸包上的点
  • 除点P外其余三点在一条直线上且垂直于x轴时,不应删除点P,因为此时点P可能时凸包上的点

#判断pi是否位于pj,pk,p0组成三角形内,返回t1,t2,t3三个变量
def isin(pi,pj,pk,p0):
    x1 = float(p0[0])
    x2 = float(pj[0])
    x3 = float(pi[0])
    x4 = float(pk[0])
    y1 = float(p0[1])
    y2 = float(pj[1])
    y3 = float(pi[1])
    y4 = float(pk[1])

    k_j0=0
    b_j0=0
    k_k0=0
    b_k0=0
    k_jk=0
    b_jk=0
    perpendicular1=False
    perpendicular2 = False
    perpendicular3 = False
    #pj,p0组成的直线,看pi,pk是否位于直线同一侧

    if x2 - x1 == 0:
    #pj,p0组成直线垂直于X轴时
        t1=(x3-x2)*(x4-x2)
        perpendicular1=True
    else:
        k_j0 = (y2 - y1) / (x2 - x1)
        b_j0 = y1 - k_j0 * x1
        t1 = (k_j0 * x3 - y3 + b_j0) * (k_j0 * x4 - y4 + b_j0)

    #pk,p0组成的直线,看pi,pj是否位于直线同一侧

    if x4 - x1 == 0:
    #pk,p0组成直线垂直于X轴时
        t2=(x3-x1)*(x2-x1)
        perpendicular2=True
    else:
        k_k0 = (y4 - y1) / (x4 - x1)
        b_k0 = y1 - k_k0 * x1
        t2 = (k_k0 * x3 - y3 + b_k0) * (k_k0 * x2 - y2 + b_k0)

    # pj,pk组成的直线,看pi,p0是否位于直线同一侧

    if x4 - x2 == 0:
    # pj,pk组成直线垂直于X轴时
        t3=(x3-x2)*(x1-x2)
        perpendicular3 = True
    else:
        k_jk = (y4 - y2) / (x4 - x2)
        b_jk = y2 - k_jk * x2
        t3 = (k_jk * x3 - y3 + b_jk) * (k_jk * x1 - y1 + b_jk)
    #如果pk,p0,pj,三点位于同一直线时,不能将点删除
    if (k_j0 * x4 - y4 + b_j0)==0 and (k_k0 * x2 - y2 + b_k0)==0 and  (k_jk * x1 - y1 + b_jk)==0 :
          t1=-1
    #如果pk,p0,pj,三点位于同一直线时且垂直于X轴,不能将点删除
    if perpendicular1 and perpendicular2 and perpendicular3:
          t1=-1

    return t1,t2,t3

接下来是实现算法主要部分,用来找出凸包上的点


import  isintriangle

def force(lis,n):
    #集合S中点个数为3时,集合本身即为凸包集
    if n==3:
        return  lis
    else:
        #集合按纵坐标排序,找出y最小的点p0
        lis.sort(key=lambda x: x[1])
        p0=lis[0]
        #除去p0的其余点集合lis_brute
        lis_brute=lis[1:]
        #temp是用来存放集合需要删除的点在lis_brute内的索引,四个点中如果有一个点在其余三个点组成的三角形内部,则该点一定不是凸包上的点
        temp=[]
        #三重循环找到所有这样在三角形内的点
        for i in range(len(lis_brute)-2):
            pi=lis_brute[i]
            #如果索引i已经在temp中,即pi一定不是凸包上的点,跳过这次循环
            if i in temp:
                continue
            for j in range(i+1,len(lis_brute) - 1):
                pj=lis_brute[j]
                #如果索引j已经在temp中,即pj一定不是凸包上的点,跳过这次循环
                if j in temp:
                    continue
                for k in range(j + 1, len(lis_brute)):
                    pk=lis_brute[k]

                    #如果索引k已经在temp中,即pk一定不是凸包上的点,跳过这次循环
                    if k in temp:
                        continue
                    #判断pi是否在pj,pk,p0构成的三角形内
                    (it1,it2,it3)=isintriangle.isin(pi,pj,pk,p0)
                    if it1>=0 and it2>=0 and it3>=0:
                        if i not in temp:
                           temp.append(i)  
                    # 判断pj是否在pi,pk,p0构成的三角形内
                    (jt1,jt2,jt3)=isintriangle.isin(pj,pi,pk,p0)
                    if jt1>=0 and jt2>=0 and jt3>=0:

                        if j not in temp:
                           temp.append(j)

                    # 判断pk是否在pj,pi,p0构成的三角形内
                    (kt1, kt2, kt3) = isintriangle.isin(pk, pi, pj, p0)
                    if kt1 >= 0 and kt2 >= 0 and kt3 >= 0:

                        if k not in temp:
                            temp.append(k)
       #listlast是最终选出的凸包集合
        lislast=[]
        for coor in lis_brute:
            loc = [i for i, x in enumerate(lis_brute) if x == coor]
            for x in loc:
                ploc = x
            if ploc not in temp:
                lislast.append(coor)
        #将p0加入凸包集合
        lislast.append(p0)
        return  lislast

最后将凸包集合输出就不多说了,按照伪码上实现就可以,凸包蛮力算法在点集大小为1000时结果


凸包问题的蛮力算法及python实现_第1张图片

你可能感兴趣的:(凸包问题的蛮力算法及python实现)