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

結果:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM