明星算法:距离启发式

A star algorithm: Distance heuristics

我正在使用这里看到的 A 星算法(取自 http://code.activestate.com/recipes/578919-python-a-pathfinding-with-binary-heap/),但我有一个问题我不明白。

这里给出的启发式是两点之间距离的平方。我发现如果我取它的平方根,我的结果会更准确,但是函数的 运行ning 时间会显着增加(即它比以前经历了很多很多的循环)。

为什么启发式的变化会导致它更准确,并且需要更长的时间才能 运行?

# Author: Christian Careaga (christian.careaga7@gmail.com)
# A* Pathfinding in Python (2.7)
# Please give credit if used

import numpy
from heapq import *


def heuristic(a, b):
    return (b[0] - a[0]) ** 2 + (b[1] - a[1]) ** 2

def astar(array, start, goal):

    neighbors = [(0,1),(0,-1),(1,0),(-1,0),(1,1),(1,-1),(-1,1),(-1,-1)]

    close_set = set()
    came_from = {}
    gscore = {start:0}
    fscore = {start:heuristic(start, goal)}
    oheap = []

    heappush(oheap, (fscore[start], start))

    while oheap:

        current = heappop(oheap)[1]

        if current == goal:
            data = []
            while current in came_from:
                data.append(current)
                current = came_from[current]
            return data

        close_set.add(current)
        for i, j in neighbors:
            neighbor = current[0] + i, current[1] + j            
            tentative_g_score = gscore[current] + heuristic(current, neighbor)
            if 0 <= neighbor[0] < array.shape[0]:
                if 0 <= neighbor[1] < array.shape[1]:                
                    if array[neighbor[0]][neighbor[1]] == 1:
                        continue
                else:
                    # array bound y walls
                    continue
            else:
                # array bound x walls
                continue

            if neighbor in close_set and tentative_g_score >= gscore.get(neighbor, 0):
                continue

            if  tentative_g_score < gscore.get(neighbor, 0) or neighbor not in [i[1]for i in oheap]:
                came_from[neighbor] = current
                gscore[neighbor] = tentative_g_score
                fscore[neighbor] = tentative_g_score + heuristic(neighbor, goal)
                heappush(oheap, (fscore[neighbor], neighbor))

    return False

'''Here is an example of using my algo with a numpy array,
   astar(array, start, destination)
   astar function returns a list of points (shortest path)'''

nmap = numpy.array([
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [1,1,1,1,1,1,1,1,1,1,1,1,0,1],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [1,0,1,1,1,1,1,1,1,1,1,1,1,1],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [1,1,1,1,1,1,1,1,1,1,1,1,0,1],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [1,0,1,1,1,1,1,1,1,1,1,1,1,1],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [1,1,1,1,1,1,1,1,1,1,1,1,0,1],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0]])

print astar(nmap, (0,0), (10,13))

Why does the change in heuristics cause it to be more accurate, and take longer to run?

第一个启发式算法,距离平方,高估了实际距离(根据情况高估了很多),即使实际距离的计算方式相同,因为实际距离计算为单个步骤的总和(平方和小于和的平方)。 A* 倾向于通过探索不足以保证找到最佳路线来对此做出回应,它更愿意继续沿着它尝试的任何路线前进,因为离目标更近一步会使到它的预期距离减少 lot(比步骤本身多得多,因为启发式高估了太多)。它通常不会备份(即从队列中取出一些先前打开的节点而不是最近的节点)并尝试其他东西,因为返回意味着 H 值上升超过 G 值下降。

所以这具有您看到的两种效果:

  1. 它通常要快得多(除了在某些迷宫中,您可以 "trick" 算法在错误的方向上运行的时间比其他情况下更长)
  2. 不一定找到最佳路线

您的连通性是 8 邻域,对此有比欧氏距离更好的启发式方法。请注意,路径不能有任意角度,它必须笔直或呈 45 度角,因此即使没有障碍物,欧氏距离也会低估距离。这对于正确性来说是可以的,但是你可以使用 "diagonal distance" 启发式:(取自 here 并且很容易适应 Python - 该站点还讨论了高估启发式的影响)

function heuristic(node) =
    dx = abs(node.x - goal.x)
    dy = abs(node.y - goal.y)
    return D * (dx + dy) + (D2 - 2 * D) * min(dx, dy)

您将 D = 1, D2 = sqrt(2) 与您的欧氏距离度量相匹配。


如果多个路径共享一个源或目标(哪一个无关紧要,因为它无论如何都是对称的),可以使用一些技术来重用一些工作。例如,当您从 A 搜索到 B 时,G 分数可以存储在网格中(它们甚至可以被排除在节点之外)。然后在搜索 towards A 的路径时,那些保存的 G 分数代表到 A 的真实距离。显然,这些可以用来获得完美的启发式算法,但还有更快的使用:如果从队列中抽取使用这种完美启发式计算其 F 的节点,则通过该节点的最短路径肯定是 (因为其 F 是 actual 路径的长度,而且它显然是最短的,因为它离开了优先级队列),而且,你知道没有进一步搜索的路径(贪婪地按照保存的 G 分数回到 A) .

这导致的是,每次搜索路径都会建立可用于另一个方向上的其他搜索的信息。另一个方向的搜索然后再次为 that 另一个方向的搜索建立信息,依此类推。应该小心一些——很容易让内存使用爆炸。可能并非所有信息都可以保留。

这可能也可以与跳跃点搜索结合使用,尽管要保存的 G 会更少,所以它可能不是很有效,主要是浪费了一堆 space。