基于python 凸包問題的解決
最近在看python的算法書,之前在年前買的書,一直在工作間隙的時候,學習充電,終于看到這本書,但是確實又有點難,感覺作者寫的代碼太炫技 了,有時候注釋也不怎么能看懂,終于想到一個方法,就是里面說的算法問題,我就百度python解決他,覺得這個挺好。
下面是凸包問題的一個代碼。
# -*- coding: utf-8 -*-import turtleimport randomimport timef=open(’point.txt’,’w’)for i in range(100): x=random.randrange(-250,250,10) y=random.randrange(-200,200,10) f.write(str(x)+’,’+str(y)+’n’)f.close()point=[] f=open(’point.txt’)for i in f: try: temp=i.split(’,’) x=float(temp[0]); y=float(temp[1]) point.append((x,y)) except : print ’a err’f.close() point=list(set(point))#去除重復的點 n=0for i in range(len(point)): if point[n][1]>point[i][1]: n=i p0=point[n]point.pop(n)def compare(a,b): x=[a[0]-p0[0],a[1]-p0[1]] y=[b[0]-p0[0],b[1]-p0[1]] dx=(x[0]**2+x[1]**2)**0.5 dy=(y[0]**2+y[1]**2)**0.5 cosa=x[0]/dx cosb=y[0]/dy if cosa < cosb: return 1 elif cosa > cosb: return -1 else: if dx<dy: return -1 elif dx>dy: return 1 else: return 0 point.sort(compare)point.insert(0,p0)ep=point[:]#復制元素,操作ep不會對point產生影響tag=0while tag==0: tag=1 l=len(ep) for i in range(l): i1,i2,i3=(i,(i+1)%l,(i+2)%l) x,y,z=(ep[i1],ep[i2],ep[i3]) a1,a2=((y[0]-x[0],y[1]-x[1]),(z[0]-y[0],z[1]-y[1])) if a1[0]*a2[1]-a1[1]*a2[0] < 0: tag=0 ep.pop(i2) break elif a1[0]*a2[1]-a1[1]*a2[0]==0 and a1[0]*a2[0]<0: #==0應改寫,360度的情況 tag=0 ep.pop(i2) break def drawpoint(point,color,line): p=turtle.getturtle() p.hideturtle() turtle.delay(1) if(line==’p’): p.speed(0) for i in point: p.pu() p.color(color) p.goto(i) p.pd() p.dot() elif(line==’l’): p.pu() p.speed(1) for i in point: p.color(color) p.goto(i) p.pd() p.dot() p.goto(point[0]) drawpoint(point,’black’,’p’)drawpoint(ep,’red’,’l’)time.sleep(1)
補充知識:凸包問題的蠻力算法及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 randomimport itertoolsdef 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 isintriangledef 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 凸包問題的解決就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支持好吧啦網。
相關文章: