1. 程式人生 > 程式設計 >基於python 凸包問題的解決

基於python 凸包問題的解決

最近在看python的演算法書,之前在年前買的書,一直在工作間隙的時候,學習充電,終於看到這本書,但是確實又有點難,感覺作者寫的程式碼太炫技 了,有時候註釋也不怎麼能看懂,終於想到一個方法,就是裡面說的演算法問題,我就百度python解決他,覺得這個挺好。

下面是凸包問題的一個程式碼。

# -*- 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))#去除重複的點
 
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[:]#複製元素,操作ep不會對point產生影響
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應改寫,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,Pk組成的三角形內部或邊界上,則P不是S的凸包頂點。

那麼如何判斷點P在三角形內部或邊界上?給定平面兩點AB,直線方程g(A,B,P)=0時,P位於直線上,g(A,P)>0和g(A,P)<0時,P分別位於直線的兩側。

判斷點P在三角形內部或邊界上只需依次檢查P和三角形的每個頂點是否位於三角形另外兩個頂點確定的直線的同一側,即判斷:

t1=g(pj,pk,p)*g(pj,pi)>=0,

t2=g(pi,p)*g(pi,pj)>=0,
t3=g(pj,pi,pk)>=0

是否同時成立

凸包問題的蠻力演算法虛擬碼如下:

BruteForce(S):

輸入:平面n個點的集合S

輸出:按逆時針順序輸出S的凸包的所有頂點

If n=3 Then 以逆時針順序輸出S的頂點,演算法結束 找到S中縱座標最小的點P,該點一定位於凸包上

For S中任意三點Pi,Pk Do If Pi,Pk 一點位於其他兩點與P構成的三角形內 Then 刪除該點

找出S中橫座標最小的點A和橫座標最小的點B

將S劃分問直線AB上方點集SU,直線AB下方點集SL,A,B兩點屬於SL

將SL按橫座標遞增排序,SU按橫座標遞減排序順序輸出SL,SU

首先隨機生成點集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

判斷點P在三角形內部或邊界上,即判斷點P是否在凸包上

在具體的判斷過程中,尤其時座標點比較密集的情況下,還有有三種比較特殊的情況

組成直線的兩點垂直於x軸

除點P外其餘三點在一條直線上時,不應刪除點P,因為此時點P可能時凸包上的點

除點P外其餘三點在一條直線上且垂直於x軸時,不應刪除點P,因為此時點P可能時凸包上的點

#判斷pi是否位於pj,p0組成三角形內,返回t1,t2,t3三個變數
def isin(pi,pj,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,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,t3

接下來是實現演算法主要部分,用來找出凸包上的點

import isintriangle

def 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,p0構成的三角形內
     (it1,it2,it3)=isintriangle.isin(pi,p0)
     if it1>=0 and it2>=0 and it3>=0:
      if i not in temp:
       temp.append(i) 
     # 判斷pj是否在pi,p0構成的三角形內
     (jt1,jt2,jt3)=isintriangle.isin(pj,p0)
     if jt1>=0 and jt2>=0 and jt3>=0:

      if j not in temp:
       temp.append(j)

     # 判斷pk是否在pj,p0構成的三角形內
     (kt1,kt2,kt3) = isintriangle.isin(pk,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 凸包問題的解決

以上這篇基於python 凸包問題的解決就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支援我們。