【问题标题】:Implementing Bidirectional A* Shortest Path Algorithm实现双向 A* 最短路径算法
【发布时间】:2015-09-17 06:44:00
【问题描述】:

我正在实现对称双向 A* 最短路径算法,如 [Goldberg and Harrelson,2005] 中所述。这只是为了理解算法,因此我使用了最基本的版本,没有任何优化步骤。

我的问题是双向算法扫描的边数几乎是测试图上单向 A* 搜索中扫描边数的两倍。

示例:使用 A*(左)和双向 A*(右)对道路网络进行 s-t 查询。前向和后向搜索扫描的节点分别用红色和绿色着色。启发式函数只是到 t 或 s 的欧几里得距离。计算的路径(蓝色)在两个图中都是正确的。

我可能对算法逻辑的理解有误。这是我如何将单向 A* 调​​整为双向(来自参考)

  • 在前向搜索和后向搜索之间交替。保持变量mu 作为当前对 s-t 路径长度的最佳估计。
  • 在前向搜索中,如果边(v,w)中的w已经被后向搜索扫描到,不要更新w的标签。
  • 每次正向搜索扫描(v,w) 并且如果w 已被反向搜索扫描,则更新mu 如果dist(s,v) + len(v,w)+dist(w,t)<= mu
  • 停止条件:当正向搜索即将扫描顶点vdist(s,v) + potential_backward(v) >= mu
  • (类似规则适用于向后搜索)

如果有人能指出我的实现中的缺陷,或者对双向 A* 算法进行更详细的解释,我将不胜感激。

Python 代码:

""" 
bidirectional_a_star: bidirectional A* search 

g: input graph (networkx object)
s, t: source and destination nodes
pi_forward, pi_backward: forward and backward potential function values
wt_attr: attribute name to be used as edge weight 
"""

def bidirectional_a_star(g,s,t,pi_forward, pi_backward, wt_attr='weight' ):
    # initialization 
    gRev = g.reverse() # reverse graph        
    ds =   { v:float('inf') for v in g } # best distances from s or t
    dt = ds.copy()
    ds[s]=0
    dt[t]=0  
    parents = {} # predecessors in forward/backward search
    parentt = {}
    pqueues =[(ds[s]+pi_forward[s],s)]  # priority queues for forward/backward search
    pqueuet = [(dt[t]+pi_backward[t],t)]

    mu = float('inf') # best s-t distance

    scanned_forward=set() # set of scanned vertices in forward/backward search
    scanned_backward=set()

    while (len(pqueues)>0 and len(pqueuet)>0):
        # forward search
        (priority_s,vs) = heappop(pqueues) # vs: first node in forward queue

        if (priority_s >= mu): # stop condition
            break

        for w in g.neighbors(vs): # scan outgoing edges from vs
            newDist = ds[vs] + g.edge[vs][w][wt_attr]            

            if (ds[w] > newDist and w not in scanned_backward):                
                ds[w] = newDist  # update w's label
                parents[w] = vs
                heappush(pqueues, (ds[w]+pi_forward[w] , w) )

            if ( (w in scanned_backward) and  (newDist + dt[w]<mu)):
                 mu = newDist+dt[w]

        scanned_forward.add(vs)  # mark vs as "scanned" 

        # backward search
        (priority_t,vt) = heappop(pqueuet) # vt: first node in backward queue

        if (priority_t>= mu ):  
            break

        for w in gRev.neighbors(vt): 
            newDist = dt[vt] + gRev.edge[vt][w][wt_attr]

            if (dt[w] >= newDist and w not in scanned_forward):
                if (dt[w] ==newDist and parentt[vt] < w):
                    continue
                else:
                    dt[w] = newDist
                    parentt[w] = vt
                    heappush(pqueuet,(dt[w]+pi_backward[w],w))
            if ( w in scanned_forward and  newDist + ds[w]<= mu):
                 mu = newDist+dt[w]

        scanned_backward.add(vt)

    # compute s-t distance and shortest path
    scanned = scanned_s.intersection(scanned_t)    
    minPathLen = min( [ ds[v]+dt[v] for v in scanned ] ) # find s-t distance   
    minPath = reconstructPath(ds,dt,parents,parentt,scanned) # join s-v and v-t path

    return (minPathLen, minPath)

更新

Janne's comment 之后,我创建了一个demo 来测试几个示例的搜索。改进了实现,扫描的节点更少。

示例:(有向)网格图上从红点到绿点的最短路径。中间的图高亮了A*扫描的节点;右图显示了前向搜索扫描的节点(橙色)和后向搜索扫描的节点(蓝色)

但是,在路网上,前向搜索扫描到的节点和后向搜索扫描到的节点的并集仍然大于单向搜索扫描到的节点数。也许这取决于输入图?

【问题讨论】:

  • 你如何记录/记录访问的边缘?
  • 算法在顶点而不是边上添加标签。例如我使用scanned_forwardscanned_backward 来存储扫描/已解决的顶点。我还将每个节点的前身存储在parentsparentt 中的两个最短路径树上。这些节点到源或目的地的距离存储在dsdt
  • 我不确定这是否是您的问题的原因,但是您对 dsdt (ds = dt = { v:float('inf') for v in g }) 的初始化使它们都引用了同一个字典。
  • 感谢您发现这一点,但实际上我只将它们写在一条指令中以节省发布空间。在原始代码中,变量是单独初始化的。我会在我的问题中改变它
  • 你用小图测试过吗?您能否发布一个包含小而简单数据的可运行示例?

标签: python algorithm shortest-path bidirectional


【解决方案1】:

嘿,你的问题是,你没有设置正确的停止条件,停止条件是什么时候(向前和向后搜索满足),

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-02-06
    • 2017-08-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多