给定一组位置和单个位置,找到从该集合到单个的最近位置

时间:2013-06-07 08:33:45

标签: python performance ironpython

给定一组位置和单个位置,从集合中找到最接近单个位置的位置。它不是通过节点找到路径;它是关于鸟瞰的距离。

位置是“节点”的属性,(它用于有限元软件扩展)。问题是:这需要很长时间。我正在寻找更快的东西。一个用户必须在一组100万个位置上调用此功能多达500次(具有不同的单个位置)(该组保持不变)。

在进行此计算之前,我宁愿不限制该集合;我不必查询数据库或任何东西;我觉得这个简单的算术应该在几毫秒内完成。我不明白为什么需要这么长时间。

# excerpt of how LocationByNodeId looks like. 40k keys is a small model, can contain up to a million keys.
node_location_by_nodeId = {43815: (3.2835714285714266, -1.8875000000000068, 0.23571428571420952), 43816: (3.227857142857142, -1.8875000000000068, 0.23571428571421035)}
location_in_space=(1,3,7)

def node_closest_to_location_in_space(location_in_space):
    global node_location_by_nodeId
    distances = {}
    for NodeId in node_location_by_nodeId:
        NodeLocation = node_location_by_nodeId[NodeId]
        distances[NodeId] = (NodeLocation[0] - location_in_space[0])**2 + 
                            (NodeLocation[1] - location_in_space[1])**2 + 
                            (NodeLocation[2] - location_in_space[2])**2
    return min(distances, key=distances.get) # I don't really get this statement, i got it from here. Maybe this one is slow?

node_closest_to_location_in_space(location_in_space)

编辑:从以下答案中获取的解决方案将运行时间减少到大数据集中原始运行时的35%(400次调用超过120万次)。

closest_node = None
closest_distance = 1e100  # An arbitrary, HUGE, value
x,y,z = location_in_space[:3]
for NodeId, NodeLocation in LocationByNodeId.iteritems():
    distance = (NodeLocation[0] - x)**2 + (NodeLocation[1] - y)**2 + (NodeLocation[2] - z)**2
    if distance < closest_distance:
        closest_distance = distance
        closest_node = NodeId
return closest_node

3 个答案:

答案 0 :(得分:1)

您无法对未排序的字典进行简单的线性搜索,并期望它快速(至少不是非常快)。 有这么多的算法可以帮助您以一种非常优化的方式解决这个问题。

建议的R-Tree是存储您的位置的完美数据结构。

您还可以在此维基百科页面中查找解决方案:Nearest Neighbor Search

答案 1 :(得分:0)

每次运行此功能时,您正在创建并销毁包含一百万个项目的字典(distances),但这甚至不是必需的。试试这个:

def node_closest_to_location_in_space(location_in_space)
    global node_location_by_nodeId
    closest_node = None
    closest_distance = 1e100  # An arbitrary, HUGE, value
    for NodeId, NodeLocation in node_location_by_nodeId.iteritems():
        distance = (NodeLocation[0] - location_in_space[0])**2 + 
                   (NodeLocation[1] - location_in_space[1])**2 + 
                   (NodeLocation[2] - location_in_space[2])**2
        if distance <= closest_distance:
            closest_distance = distance
            closest_node = NodeId
    return (closest_node, closest_distance)

我相信每次调用该函数时创建和删除distances dict所涉及的开销都会导致您的性能下降。如果是这样,这个版本应该更快。

答案 2 :(得分:0)

索引到您的位置参数需要花费时间,并且所有百万个节点的位置都不会发生变化,因此请将这些不变量提升到for循环之外:

for NodeId, NodeLocation in node_location_by_nodeId.iteritems():
    distance = (NodeLocation[0] - location_in_space[0])**2 + 
               (NodeLocation[1] - location_in_space[1])**2 + 
               (NodeLocation[2] - location_in_space[2])**2
    if distance <= closest_distance:
        closest_distance = distance
        closest_node = NodeId

变为:

x,y,z = location_in_space
for NodeId, NodeLocation in node_location_by_nodeId.iteritems():
    distance = (NodeLocation[0] - x)**2 + 
               (NodeLocation[1] - y)**2 + 
               (NodeLocation[2] - z)**2
    if distance <= closest_distance:
        closest_distance = distance
        closest_node = NodeId

现在这些变为简单(和更快)的本地值引用。

您还可以尝试使用math.hypot调用替换距离计算,该调用是在快速C代码中实现的:

from math import hypot

x,y,z = location_in_space
for NodeId, NodeLocation in node_location_by_nodeId.iteritems():
    distance = hypot(hypot((NodeLocation[0] - x), (NodeLocation[1] - y)),(NodeLocation[2] - z))
    if distance <= closest_distance:
        closest_distance = distance
        closest_node = NodeId

hypot仅写入2D距离计算,因此要做3D,您必须调用hypot(hypot(xdist,ydist),zdist)。)