基于python 凸包問題的解決
最近在看python的算法書,之前在年前買的書,一直在工作間隙的時(shí)候,學(xué)習(xí)充電,終于看到這本書,但是確實(shí)又有點(diǎn)難,感覺作者寫的代碼太炫技 了,有時(shí)候注釋也不怎么能看懂,終于想到一個(gè)方法,就是里面說的算法問題,我就百度python解決他,覺得這個(gè)挺好。
下面是凸包問題的一個(gè)代碼。
# -*- coding: utf-8 -*-
import turtle
import random
import time
f=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))#去除重復(fù)的點(diǎn)
n=0
for 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[:]#復(fù)制元素,操作ep不會(huì)對point產(chǎn)生影響
tag=0
while 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應(yīng)改寫,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)
補(bǔ)充知識:凸包問題的蠻力算法及python實(shí)現(xiàn)
蠻力法的基本思想是先用排除法確定凸包的頂點(diǎn),然后按逆時(shí)針順序輸出這些頂點(diǎn)。在判斷點(diǎn)P是不是凸包上的頂點(diǎn)時(shí),有如下性質(zhì):
給定平面點(diǎn)集S,P,Pi,Pj,Pk是S中四個(gè)不同的點(diǎn),如果P位于Pi,Pj,Pk組成的三角形內(nèi)部或邊界上,則P不是S的凸包頂點(diǎn)。
那么如何判斷點(diǎn)P在三角形內(nèi)部或邊界上?給定平面兩點(diǎn)AB,直線方程g(A,B,P)=0時(shí),P位于直線上,g(A,B,P)>0和g(A,B,P)<0時(shí),P分別位于直線的兩側(cè)。
判斷點(diǎn)P在三角形內(nèi)部或邊界上只需依次檢查P和三角形的每個(gè)頂點(diǎn)是否位于三角形另外兩個(gè)頂點(diǎn)確定的直線的同一側(cè),即判斷:
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
是否同時(shí)成立
凸包問題的蠻力算法偽代碼如下:
BruteForce(S):
輸入:平面n個(gè)點(diǎn)的集合S
輸出:按逆時(shí)針順序輸出S的凸包的所有頂點(diǎn)
If n=3 Then 以逆時(shí)針順序輸出S的頂點(diǎn),算法結(jié)束 找到S中縱坐標(biāo)最小的點(diǎn)P,該點(diǎn)一定位于凸包上
For S中任意三點(diǎn)Pi,Pj,Pk Do If Pi,Pj,Pk 一點(diǎn)位于其他兩點(diǎn)與P構(gòu)成的三角形內(nèi) Then 刪除該點(diǎn)
找出S中橫坐標(biāo)最小的點(diǎn)A和橫坐標(biāo)最小的點(diǎn)B
將S劃分問直線AB上方點(diǎn)集SU,直線AB下方點(diǎn)集SL,A,B兩點(diǎn)屬于SL
將SL按橫坐標(biāo)遞增排序,SU按橫坐標(biāo)遞減排序順序輸出SL,SU
首先隨機(jī)生成點(diǎn)集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
判斷點(diǎn)P在三角形內(nèi)部或邊界上,即判斷點(diǎn)P是否在凸包上
在具體的判斷過程中,尤其時(shí)坐標(biāo)點(diǎn)比較密集的情況下,還有有三種比較特殊的情況
組成直線的兩點(diǎn)垂直于x軸
除點(diǎn)P外其余三點(diǎn)在一條直線上時(shí),不應(yīng)刪除點(diǎn)P,因?yàn)榇藭r(shí)點(diǎn)P可能時(shí)凸包上的點(diǎn)
除點(diǎn)P外其余三點(diǎn)在一條直線上且垂直于x軸時(shí),不應(yīng)刪除點(diǎn)P,因?yàn)榇藭r(shí)點(diǎn)P可能時(shí)凸包上的點(diǎn)
#判斷pi是否位于pj,pk,p0組成三角形內(nèi),返回t1,t2,t3三個(gè)變量 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是否位于直線同一側(cè) if x2 - x1 == 0: #pj,p0組成直線垂直于X軸時(shí) 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是否位于直線同一側(cè) if x4 - x1 == 0: #pk,p0組成直線垂直于X軸時(shí) 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是否位于直線同一側(cè) if x4 - x2 == 0: # pj,pk組成直線垂直于X軸時(shí) 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,三點(diǎn)位于同一直線時(shí),不能將點(diǎn)刪除 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,三點(diǎn)位于同一直線時(shí)且垂直于X軸,不能將點(diǎn)刪除 if perpendicular1 and perpendicular2 and perpendicular3: t1=-1 return t1,t2,t3
接下來是實(shí)現(xiàn)算法主要部分,用來找出凸包上的點(diǎn)
import isintriangle
def force(lis,n):
#集合S中點(diǎn)個(gè)數(shù)為3時(shí),集合本身即為凸包集
if n==3:
return lis
else:
#集合按縱坐標(biāo)排序,找出y最小的點(diǎn)p0
lis.sort(key=lambda x: x[1])
p0=lis[0]
#除去p0的其余點(diǎn)集合lis_brute
lis_brute=lis[1:]
#temp是用來存放集合需要?jiǎng)h除的點(diǎn)在lis_brute內(nèi)的索引,四個(gè)點(diǎn)中如果有一個(gè)點(diǎn)在其余三個(gè)點(diǎn)組成的三角形內(nèi)部,則該點(diǎn)一定不是凸包上的點(diǎn)
temp=[]
#三重循環(huán)找到所有這樣在三角形內(nèi)的點(diǎn)
for i in range(len(lis_brute)-2):
pi=lis_brute[i]
#如果索引i已經(jīng)在temp中,即pi一定不是凸包上的點(diǎn),跳過這次循環(huán)
if i in temp:
continue
for j in range(i+1,len(lis_brute) - 1):
pj=lis_brute[j]
#如果索引j已經(jīng)在temp中,即pj一定不是凸包上的點(diǎn),跳過這次循環(huán)
if j in temp:
continue
for k in range(j + 1, len(lis_brute)):
pk=lis_brute[k]
#如果索引k已經(jīng)在temp中,即pk一定不是凸包上的點(diǎn),跳過這次循環(huán)
if k in temp:
continue
#判斷pi是否在pj,pk,p0構(gòu)成的三角形內(nèi)
(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構(gòu)成的三角形內(nèi)
(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構(gòu)成的三角形內(nèi)
(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
最后將凸包集合輸出就不多說了,按照偽碼上實(shí)現(xiàn)就可以,凸包蠻力算法在點(diǎn)集大小為1000時(shí)結(jié)果

以上這篇基于python 凸包問題的解決就是小編分享給大家的全部內(nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
python3.x編碼解碼unicode字符串的實(shí)現(xiàn)示例
ASCII文本編碼是一種Unicode,存儲(chǔ)為表示字符的字節(jié)值的一個(gè)序列,本文主要介紹了python3.x編碼解碼unicode字符串的實(shí)現(xiàn)示例,具有一定的參考價(jià)值,感興趣的可以了解一下2024-01-01
使用python?itertools實(shí)現(xiàn)計(jì)算雙十一滿減湊單
一年一度的雙十一又到了,在這樣一個(gè)日子中,可能遇到一些問題,首先是“湊單”問題,本文將使用python中的itertools庫解決這一問題,感興趣的可以了解下2024-11-11
Python虛擬環(huán)境virtualenv的安裝與使用詳解
virtualenv可以用來管理互不干擾的獨(dú)立python虛擬環(huán)境,在有些場景下非常有用,下面這篇文章主要給大家介紹了Python虛擬環(huán)境virtualenv安裝與使用的相關(guān)資料,需要的朋友可以參考借鑒,下面來一起看看吧。2017-05-05
python中isoweekday和weekday的區(qū)別及說明
這篇文章主要介紹了python中isoweekday和weekday的區(qū)別及說明,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07
python實(shí)現(xiàn)監(jiān)控windows服務(wù)并自動(dòng)啟動(dòng)服務(wù)示例
這篇文章主要介紹了python實(shí)現(xiàn)監(jiān)控windows服務(wù)并自動(dòng)啟動(dòng)服務(wù)示例,需要的朋友可以參考下2014-04-04

