python学习5---实现凸包

1、暴力法

  1 def g(A,B,P):
  2     """
  3     判断点PA矢量在AB矢量的顺时针还是逆时针方向,
  4     若在逆时针方向则返回1,同向返回0,在顺时针方向返回-1
  5     :param A:
  6     :param B:
  7     :param P:
  8     :return: 1或0或-1
  9     """
 10     #使用PxQ=XpYq-XqYp,若大于0则表示Q在P的逆时针方向
 11     result = (P[1]-A[1])*(B[0]-A[0])-(B[1]-A[1])*(P[0]-A[0])
 12     if result<0:
 13         return -1
 14     elif result==0:
 15         return 0
 16     else:
 17         return 1
 18 
 19 def isInTriangle(Pi,Pj,Pk,P):
 20     """
 21     判断点P是否在其他三个点组成的三角形中,是的话返回true
 22     :param P:
 23     :param Pi:
 24     :param Pj:
 25     :param Pk:
 26     :return:
 27     """
 28     if g(Pi,Pj,Pk)==0:
 29         return 0
 30     if g(Pi,Pj,P)*g(Pi,Pj,Pk)>=0 and g(Pj,Pk,P)*g(Pj,Pk,Pi)>=0 and g(Pk,Pi,P)*g(Pk,Pi,Pj)>=0:
 31         return 1
 32     return 0
 33 
 34 def bruteForce(S):
 35     """
 36     暴力方法求解凸包
 37     :param S: 输入的顶点集合
 38     :return: Q的凸包
 39     """
 40     n=len(S)
 41     flag=ones((n,1))
 42     output = []
 43     if n==3:
 44         #以逆时针方式输出Q的点
 45         if g(S[0], S[1], S[2])>0:
 46             output.append(S[0])
 47             output.append(S[1])
 48             output.append(S[2])
 49 
 50         elif g(S[0], S[1], S[2])<0:
 51             output.append(S[2])
 52             output.append(S[1])
 53             output.append(S[0])
 54         return output
 55     # 若取得的三个点共线怎么办?????
 56     for i in range(n - 3):
 57         for j in range(i + 1, n - 2):
 58             for k in range(j + 1, n - 1):
 59                 for p in range(k + 1, n):
 60                     # 有一个点在其他点组成的三角形里
 61                     if isInTriangle(S[i], S[j], S[k], S[p]):
 62                         # 则将该点对应的标志位置为0
 63                         flag[p] = 0
 64                     if isInTriangle(S[p], S[j], S[k], S[i]):
 65                         # 则将该点对应的标志位置为0
 66                         flag[i] = 0
 67                     if isInTriangle(S[k], S[p], S[i], S[j]):
 68                         # 则将该点对应的标志位置为0
 69                         flag[j] = 0
 70                     if isInTriangle(S[p], S[j], S[i], S[k]):
 71                         # 则将该点对应的标志位置为0
 72                         flag[k] = 0
 73     print(flag)
 74     sub_S = []
 75     for i in range(n):
 76         if flag[i][0]:  # if标志不为0,将添加到sub_S:
 77             sub_S.append(S[i])  # 则sub_S保存的是在凸包的顶点
 78 
 79     sub_S = np.array(sub_S)
 80     # 找到sub_S的x坐标最大点B和x坐标最小点A
 81     sub_S_index = argsort(sub_S[:, 0])
 82     A = sub_S[sub_S_index[0]]
 83     B = sub_S[sub_S_index[-1]]
 84 
 85     # 按照点在AB直线上方还是下方将sub_S分为两部分Sup,Sdown
 86     Sup = []
 87     Sdown = []
 88     for i in range(len(sub_S)):
 89         if g(A, B, sub_S[i]) > 0:
 90             Sup.append(sub_S[i])
 91         if g(A, B, sub_S[i]) < 0:
 92             Sdown.append(sub_S[i])
 93 
 94     # 将Sup按照横坐标递减排序,将Sdown按照横坐标递增排序
 95     Sup = np.array(Sup)
 96     Sup = Sup[argsort(-Sup[:, 0])]
 97     Sdown = np.array(Sdown)
 98     Sdown = Sdown[argsort(Sdown[:, 0])]
 99 
100     # 从B开始按照x坐标递减依次输出Sup,到达A,按照x坐标从小到大依次输出Sdown
101     output=[]
102     # for i in range(len(sub_S)):
103     output.append(A)
104     output.extend(Sdown)
105     output.append(B)
106     output.extend(Sup)
107     return output

2、GrahamScan

def GrahamScan(S):
 2     """
 3     GrahamScan求凸包
 4     :param S:
 5     :return:
 6     """
 7     #预处理:找到S中y坐标最小的点P0,以水平为极轴求得每个点极角
 8     n = len(S)
 9     P = []
10 
11     S = S[argsort(S[:, 1])]
12     #P.append(tuple(S[0]))
13     P.append(list(S[0, 0:2]))
14     PointPolar = []  # 保存(x,y,极角)
15     for i in range(1, n):
16         polar = math.atan2(S[i][1] - S[0][1], S[i][0] - S[0][0])
17         polar = polar / math.pi * 180
18         PointPolar.append([S[i][0], S[i][1], polar])
19     # 将PointPolar的点按照极角从小到大排序,保存在result
20     result = preProcessing(PointPolar)
21 
22     new_dict2 = remove_dup(result, P[0])
23     #P.extend(new_dict2.keys())
24     for key in new_dict2:
25         P.append(list(key))
26     #若m<=1返回凸包是空
27     m=len(P)
28     if m<=2:
29         return
30     # 将P[0],P[1],P[2]依次压栈Q
31     stack = []
32     stack.append(P[0])
33     stack.append(P[1])
34     stack.append(P[2])
35     for i in range(3, m):
36         while isInTriangle(P[0], P[i], stack[-2], stack[-1]):
37             stack.pop()
38         stack.append(P[i])
39     return stack
 1 def preProcessing(PointPolar):
 2     """
 3     当多个点的极角相同时,保留距离原点最远的点
 4     :param dict:
 5     :return:一个list,经预处理的P[0:m],按照极角从小到大保存要处理的点集
 6     """
 7     sorted_polar=sorted(PointPolar,key=lambda d:d[2])
 8     return sorted_polar
 9 
10 
11 def remove_dup(sorted_polar,raw):
12     """
13     :param sorted_dict:
14     :return:
15     """
16     sorted_dict = {}
17     for d in sorted_polar:
18         sorted_dict[(d[0], d[1])] = d[2]
19     new_dict = {}
20     new_dict2 = {}
21     for k, v in sorted_dict.items():
22         new_dict.setdefault(v, []).append(k)
23     for k, v in new_dict.items():
24         if len(v) > 1:
25             d = []
26             for item in v:
27                 d.append((item[0]-raw[0]) * (item[0]-raw[0]) + (item[1]-raw[1]) * (item[1]-raw[1]))
28             v = v[argmax(d)]
29             new_dict2[v] = k
30         else:
31             new_dict2[v[0]] = k
32     return new_dict2

结果:

 

posted @ 2019-04-22 19:07  小新新的蜡笔  阅读(1143)  评论(0编辑  收藏  举报