我目前正在构建一个基于位置的服务,该服务计算用户将汽车共享给特定事件的路线。为了计算最短距离,需要知道用户之间的行驶距离,因为系统的一个限制是每个驾驶员不应超过一定距离以便接载特定乘客。为避免在同一路线上两次调用Google Maps API,我会在程序开头填充一个Dict来存储距离。距离的生成如下:
def generateDistances(self):
users = self.drivers + self.passengers
for user1 in users:
for user2 in users:
if user1 != user2:
distance = GetDistance(user1.location, user2.location)
self.distances.append({'Start' : user1, 'End' : user2, 'Distance' : distance['Distance']['meters'], 'Duration': distance['Duration']['seconds']})
self.distances.append({'Start' : user1, 'End' : self.destination, 'Distance' : distance['Distance']['meters'], 'Duration': distance['Duration']['seconds']})
GetDistance方法只根据其纬度和经度从Google Maps API获取两个位置之间的路线。程序然后调用以下函数在Dict中找到距离:
def getSavedDistance(self, user1, user2):
if user1 == user2:
return 0
for record in self.distances:
if record['Start'] == user1:
if record['End'] == user2:
return record['Distance']
logging.warn("No distance from %s to %s found" % (user1.userid, user2.userid))
但是,我一直在谷歌应用引擎上运行它并且运行速度非常慢,并且您可以想象随着问题规模的增加(即更多用户),运行时间呈指数级增长。我想要做的是用每个用户之间的直线距离初始化dict(以数学方式计算,不需要API调用),并且当系统测试路径的长度时,它将首先测试直线距离。如果直线距离大于最大距离,则路线太长 - 不需要计算实际距离。否则,系统只会看到驱动距离不在dict中,并进行必要的API调用以将其放入其中。
所以,我想出了这样的东西来初始化距离(请注意,这不起作用,因为我无法在dict值中插入null):
def initialiseDistances(self):
users = self.drivers + self.passengers
for user1 in users:
for user2 in users:
if user1 != user2:
self.distances.append({'Start' : user1, 'End' : user2, 'Distance' : null, 'Duration' : null, 'StraightLine' : GetStraightLineDistance(user1.location, user2.location)})
self.distances.append({'Start' : user1, 'End' : self.destination, 'Distance' : null, 'Duration' : null, 'StraightLine' : GetStraightLineDistance(user1.location, self.destination)})
...然后可以将getSavedDistance方法更改为:
def getSavedDistance(self, user1, user2):
if user1 == user2:
return 0
for record in self.distances:
if record['Start'] == user1:
if record['End'] == user2:
if record['Distance'] == null:
distance = GetDistance(user1.location, user2.location)
record['Distance'] = distance['Distance']['meters']
record['Duration'] = distance['Duration']['seconds']
return record['Distance']
logging.warn("No distance from %s to %s found" % (user1.userid, user2.userid))
这将允许系统仅填充实际使用的距离值,并避免两次进行相同的API调用。但是,显然我不能将null插入到dict值中。有没有人有一个想法,我可以在这个字典中插入一些值,告诉我距离没有价值呢?
由于
答案 0 :(得分:2)
由于这是Python,None
是空值。使用None
与is None
进行比较,而不是== None
。
答案 1 :(得分:2)
让您的self.distances
字典将(start_user,end_user)元组映射到您想要的信息。你正在做的事情涉及O(N)访问列表项只是为了一次查找,而只是1次dict查找。使用dict,如果您没有(user1,user2)的任何信息,则不需要浪费时间和内存将虚拟的“null”条目放入数据结构中。
info = self.distances_DICT.get((user1, user2))
if info is None:
self.calculate_the_distance_or_whatever_else_you_need_to_do(user1, user2))
答案 2 :(得分:1)
我可以建议采用不同的方法吗?使用(user1,user2)使你的self.distance成为一个字典,它将你的查找从O(n)更改为O(1)。假设GetDistance(user1, user2)
与GetDistance(user2, user1)
相同,您可以确保将用作字典键的每个元组排序,以便您可以为每个方向重复使用相同的值。
扩展John Machin的观点,在Python中编写类似内容的惯用方法可能如下:
class DistanceFinder(object):
distances = {}
def GetDistance(self, user1, user2):
userkey = (user1, user2)
if userkey in self.distances:
return self.distances[userkey]
result = [... calculations go here ...]
self.distances[userkey] = result
return result
Python 3.2中的有趣工作:
from functools import lru_cache
class DistanceFinder:
@lru_cache(maxsize=None)
def GetDistance(self, user1, user2):
return [... calculations go here ...]
内置的缓存内容。很好,是吗?