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
結果:

