Python-加速寻路

Python-加速寻路,python,path-finding,Python,Path Finding,这是我的寻路功能: def get_distance(x1,y1,x2,y2): neighbors = [(-1,0),(1,0),(0,-1),(0,1)] old_nodes = [(square_pos[x1,y1],0)] new_nodes = [] for i in range(50): for node in old_nodes: if node[0].x == x2 and node[0].y == y2:

这是我的寻路功能:

def get_distance(x1,y1,x2,y2):
    neighbors = [(-1,0),(1,0),(0,-1),(0,1)]
    old_nodes = [(square_pos[x1,y1],0)]
    new_nodes = []
    for i in range(50):
        for node in old_nodes:
            if node[0].x == x2 and node[0].y == y2:
                return node[1]
            for neighbor in neighbors:
                try:
                    square = square_pos[node[0].x+neighbor[0],node[0].y+neighbor[1]]
                    if square.lightcycle == None:
                        new_nodes.append((square,node[1]))
                except KeyError:
                    pass
        old_nodes = []
        old_nodes = list(new_nodes)
        new_nodes = []
        nodes = []
    return 50

问题是人工智能需要很长时间才能做出响应(响应时间你应该用曼哈顿距离来代替你的算法作为启发式。

一个相当快的解决方案是实施Dijkstra算法(我已经在中实现了):

构建原始地图。这是一个蒙面阵列,步行者无法在蒙面元素上行走:

%pylab inline
map_size = (20,20)
MAP = np.ma.masked_array(np.zeros(map_size), np.random.choice([0,1], size=map_size))
matshow(MAP)

下面是Dijkstra算法:

def dijkstra(V):
    mask = V.mask
    visit_mask = mask.copy() # mask visited cells
    m = numpy.ones_like(V) * numpy.inf
    connectivity = [(i,j) for i in [-1, 0, 1] for j in [-1, 0, 1] if (not (i == j == 0))]
    cc = unravel_index(V.argmin(), m.shape) # current_cell
    m[cc] = 0
    P = {}  # dictionary of predecessors 
    #while (~visit_mask).sum() > 0:
    for _ in range(V.size):
        #print cc
        neighbors = [tuple(e) for e in asarray(cc) - connectivity 
                     if e[0] > 0 and e[1] > 0 and e[0] < V.shape[0] and e[1] < V.shape[1]]
        neighbors = [ e for e in neighbors if not visit_mask[e] ]
        tentative_distance = [(V[e]-V[cc])**2 for e in neighbors]
        for i,e in enumerate(neighbors):
            d = tentative_distance[i] + m[cc]
            if d < m[e]:
                m[e] = d
                P[e] = cc
        visit_mask[cc] = True
        m_mask = ma.masked_array(m, visit_mask)
        cc = unravel_index(m_mask.argmin(), m.shape)
    return m, P

def shortestPath(start, end, P):
    Path = []
    step = end
    while 1:
        Path.append(step)
        if step == start: break
        if P.has_key(step):
            step = P[step]
        else:
            break
    Path.reverse()
    return asarray(Path)

以下是一些计时统计数据:

%timeit dijkstra(MAP)
#10 loops, best of 3: 32.6 ms per loop

代码中最大的问题是,您没有采取任何措施来避免多次访问相同的坐标。这意味着您访问的节点数肯定会呈指数增长,因为它可以在前几个节点上来回多次

避免重复的最佳方法是维护已添加到队列中的坐标的
(尽管如果您的
节点
值是可散列的,您可能可以直接将它们添加到集合中,而不是坐标元组)。由于我们正在进行广度优先搜索,我们将始终通过(其中一个)到达给定的坐标最短的路径,因此我们不必担心以后会找到更好的路径

试着这样做:

def get_distance(x1,y1,x2,y2):
    neighbors = [(-1,0),(1,0),(0,-1),(0,1)]
    nodes = [(square_pos[x1,y1],0)]
    seen = set([(x1, y1)])
    for node, path_length in nodes:
        if path_length == 50:
            break
        if node.x == x2 and node.y == y2:
            return path_length
        for nx, ny in neighbors:
            try:
                square = square_pos[node.x + nx, node.y + ny]
                if square.lightcycle == None and (square.x, square.y) not in seen:
                    nodes.append((square, path_length + 1))
                    seen.add((square.x, square.y))
            except KeyError:
                pass
    return 50
我还简化了循环。与在每个深度后切换列表不同,您可以只使用一个循环,并在迭代早期值时添加到其末尾。如果没有找到少于50步的路径(使用存储在2元组中的距离,而不是外部循环的通过次数),我仍然会中止。进一步的改进可能是对队列使用
collections.dequeue
,因为您可以从一端高效地
pop
,而将
追加到另一端。这可能不会产生很大的影响,但可能会避免一点内存使用


我还避免了大多数以1和0进行索引,以便于在
for
循环中解压成单独的变量名。我认为这更易于阅读,并且避免了混淆,因为两种不同类型的2元组具有不同的含义(一种是
节点,距离
元组,另一种是
x,y
).

它不应该是
节点[0]。y==y2
?它也应该是
新的\u节点。追加((正方形,节点[1]+1))
。请注意
+1
。这会快多少?A*和B*在性能方面相当先进。我不知道与您的实现相比,加速会有多大,但它会存在。A*主要是有用的,最适合路径查找算法。甚至最适合导航系统。
def get_distance(x1,y1,x2,y2):
    neighbors = [(-1,0),(1,0),(0,-1),(0,1)]
    nodes = [(square_pos[x1,y1],0)]
    seen = set([(x1, y1)])
    for node, path_length in nodes:
        if path_length == 50:
            break
        if node.x == x2 and node.y == y2:
            return path_length
        for nx, ny in neighbors:
            try:
                square = square_pos[node.x + nx, node.y + ny]
                if square.lightcycle == None and (square.x, square.y) not in seen:
                    nodes.append((square, path_length + 1))
                    seen.add((square.x, square.y))
            except KeyError:
                pass
    return 50