我有兩個大的點集L1和L2和一個固定點B.我需要一個有效的方法來過濾L1並刪除所有點它們到B的路徑(直線)被L2中的任何點阻塞。最快的方法來檢查列表中的任何點是否在bwteen兩個固定點A和B
我試過如下:
# Measure distance between two points
def distance (p1, p2):
return ((p2[1] - p1[1])**2 + (p2[0] - p1[0])**2)**0.5
# Check if point c is on same line between points a and b
def inBetween (a, b, c):
return distance (a, b) == distance (a, c) + distance (b, c)
def isPathBlocked (L1, L2, B):
for A in L1:
for C in L2:
if inBetween (A, B, C):
print "path blocked"
雖然它的工作原理,但它是非常CPU密集型考慮到L1和L2可以在每個有超過50,000點,併爲此嵌套循環將執行50000 * 50000次。有沒有更好的方法來做到這一點,或有什麼好的算法,我可以在這裏使用?
注:
我應該早點提及,但在L1和L2的所有點COORDS是整數。
注2:
B具有整數COORDS和在L1和L2的所有點從乙一定半徑範圍內(半徑爲約10至10000)。
解決方案:
感謝偉大的答案在下面,我在這裏加入如何與詳盡的解釋使用提供了主要的兩種方法(散列和比較,排序和合並比較)對於那些誰是新的一個例子到算法,它可能會發現它有用:
l1 = [(x,y) for x in range (10) for y in range(10)]
l2 = [(5,3), (5,1), (8,2), (4,3), (1,5), (3,5), (7,7), (3,7)]
b = (5,5)
# instead of using atan2 to calculate polar angles, to improve performance we only
# calculate (y/x, x>0) which has a unique value for different angles
# if x == 0 (angle is eigher 90 or -90 degrees) then we use (None, y>0) instead
def toPolar (p1, p2):
if p2[0] != p1[0]:
return (float (p2[1] - p1[1])/float (p2[0] - p1[0]), p2[0] < p1[0])
if p2[1] != p1[1]:
return (None, p2[1] < p1[1])
# Check if b is between a and c, we don't need to check if they
# are cllinear because they are already when they have same polar angle
def inBetween (a, b, c):
if a[0] <= b[0] <= c[0] or c[0] <= b[0] <= a[0]:
return a[1] <= b[1] <= c[1] or c[1] <= b[1] <= a[1]
return False
# b stationary point, l1 the points of interest, l2 blockers
def hashFilter (b, l1, l2):
hashTable = {}
# create a hash table of l2 points using polar angles as keys,
# if two points have the same polar angle keep only the one that
# is closer to b
for blocker in l2:
key = toPolar (b, blocker)
prev = hashTable.get (key)
if prev == None or inBetween (b, blocker, prev):
hashTable[key] = blocker
unBlocked = []
blocked = []
# Remove the points from l1 if they are blocked by any point from l2
for point in l1:
key = toPolar (b, point)
blocker = hashTable.get (key)
if blocker == None or not inBetween (b, blocker, point):
unBlocked.append (point)
else: blocked.append (point)
return sorted (blocked)
def sortFilter (b, l1, l2):
# Convert cartesian to polar (sort of)
points = [(toPolar(b, x), x) for x in l1]
# Sort using polar angle as key
points = sorted (points, key = lambda x: x[0])
# Convert cartesian to polar (sort of)
blockers = [(toPolar(b, x), x) for x in l2]
# Sort using polar angle as key
blockers = sorted (blockers, key = lambda x: x[0])
unBlocked = []
blocked = []
ind = 0
length = len (blockers)
for point in points:
if ind >= length: break
isBlocked = False
subInd = None
# Increase index of blockers until we reach a matching polar angle
while ind < length and blockers[ind][0] <= point[0]:
if blockers[ind][0] == point[0]:
# we need to check every point in points(l1) for being blocked
# by comparing it to all points in blockers(l2) with
# identical polar angle.
# However because there could be multiple points and multiple
# blockers with the same polar angle, we store the ind of the
# first identical polar angle that appears in the blockers list,
# so that the next item in points list can loop again over
# this group of blockers
if subInd == None: subInd = ind
if inBetween (b, blockers[ind][1], point[1]):
blocked.append (point[1])
isBlocked = True
# If we found out that the point is blocked then we break
# out of the loop to test the next item in points
break
ind += 1
if not isBlocked: unBlocked.append (point[1])
# Move the index back to the first item in the blockers group with
# same polar angle to the last point checked
if subInd != None: ind = subInd
return sorted (blocked)
print hashFilter (b, l1, l2)
print sortFilter (b, l1, l2)
如果您在極座標系中查看問題,該怎麼辦?你必須計算'2 * N'的角度(這一步可以並行完成);但是這些可以比你的2個循環更有效率地進行排序和比較? –
好 - 感謝基準測試的更新!將嘗試做一個小例子或我的意思。 –
C必須靠近AB線纔算在線上? –