小伙伴们在学习各类编程语言时,"Hello World !“经常被我们作为学习或调试某类语言的第一个演示程序。在我看来,Dijkstra算法寻路时的迭代逻辑简单,可以作为规划算法的这样一个"Hello World !”。那么现在,让我们把标记起点的旗子正式插上,正式开始我们的规划算法之旅。

文章目录

  • 一、背景:以我之姓冠你之名的浪漫与不朽
  • 二、原理:既贪心又最优
  • 三、Dijkstra算法伪代码
  • 四、示例代码演示(python|Jupyter notebook)
    • 4.1 图(graph)
      • 4.1.1 图数据结构
      • 4.1.2 Dijkstra算法与最短路径提取
      • 4.1.3 运行结果
    • 4.2 栅格地图
  • 五、重要知识点
  • 六、总结
  • 七、思考 (欢迎在博客下方留言)
  • 参考文献

一、背景:以我之姓冠你之名的浪漫与不朽

Dijkstra算法1作为最典型的图搜索寻路算法,用于在非负权边图(graph)中求解从起始状态节点到其他所有状态节点的最短路径——也被称为单源非负权边图最短路径问题。在很多专业课程中都能看到它的身影,如数据结构、图论、运筹学等等 。

下图显示了一个非负权边图的例子:

Dijkstra算法是由荷兰计算机科学家Edsger W. Dijkstra(1930年5月11日 – 2002年8月6日)于1959年提出,并以其发明者的名字为名2。Dijkstra早在1956年时就发明了这个算法,直到1959年才等到合适的学术期刊出现并得以发表。在Web of Science数据库中,Dijkstra算法算得上是一篇经典引文。Dijkstra对这个以他的名字命名的算法颇为得意,并在1993年的自传中回忆这一美妙时光3

最短路径算法以我的名字命名,让我出了名。当初我设计这个算法的时候,并没有拿着纸和笔冥思苦想。那是在阿姆斯特丹的一个露天咖啡馆,我和妻子(当时在谈恋爱,一年后的1957年结婚)晒着阳光喝着咖啡,然后我灵机一动想到了这个算法。

这里介绍了Dijkstra与Dijkstra算法之间背景故事,而略去介绍获得ACM图灵奖的Dijkstra,制定了堆栈、死锁、互斥、结构化编程等促进计算机软件与理论巨大进展的计算机概念与编程技术的Dijkstra。想去了解这位宗师般的人物,建议挑一个静宁的午后,泡一杯茶,然后再点开(wikipedia|Edsger W. Dijkstra或 王选 院士在Dijkstra逝后刊于《计算机世界》的纪念文——《从Dijkstra谈帅才的洞察力》4,慢慢品味吧。

过几天就是Edsger W. Dijkstra的逝世二十周年纪念日。本来想在8月6日当天发出来致敬Dijkstra。后来细想一下,自己目前还只是个无名小卒,难免有蹭巨人光环之嫌。最终选择把这份敬仰放在心里,放在这篇博客的小小缝隙中。

二、原理:既贪心又最优

定义(cost-to-come):将从起始状态(标记为 x I x_I xI​)到达状态 x x x的路径长度定义为cost-to-come,记为 C ( x ) C(x) C(x)。其中,从 x I x_I xI​到 x x x的最短路径对应的cost-to-come被称为最优cost-to-come,记为 C ∗ ( x ) C^*(x) C∗(x)。

Dijkstra算法的基本思想是采用贪心策略,也即:从未被探索过的状态节点中选择cost-to-come(用 C C C表示)值最小的作为当前探索状态节点 x x x,并根据式(1)更新当前探索状态节点的所有未被探索过的后继状态节点 x ′ x' x′的 C C C值与父节点,然后将 x x x标记为已探索,直到所有状态都已探索后输出结果,结束算法。(算法启动前,初始化 C ( x I ) = 0 C(x_I)=0 C(xI​)=0, C ( x ≠ x I ) = ∞ C(x\neq x_{I})=\infin C(x=xI​)=∞)。

< C n e w ( x ′ ) , F n e w ( x ′ ) > = { < C ( x ) + l ( x , x ′ ) , x > , if  C ( x ) + l ( x , x ′ ) < C o l d ( x ′ ) < C o l d ( x ′ ) , F o l d ( x ′ ) > , otherwise (1) <C_{new}(x'), F_{new}(x')>=\left\{ \begin{array}{lcl} <C(x)+l(x, x'), x> &, & \text{if }C(x)+l(x,x')<C_{old}(x') \\ <C_{old}(x'), F_{old}(x')> &, & \text{otherwise} \\ \end{array} \right. \tag{1} <Cnew​(x′),Fnew​(x′)>={<C(x)+l(x,x′),x><Cold​(x′),Fold​(x′)>​,,​if C(x)+l(x,x′)<Cold​(x′)otherwise​(1)

其中, x x x为当前要探索的状态节点, x ‘ x‘ x‘为 x x x的后继状态, C ( x ) C(x) C(x)为 x x x当前的cost-to-come值, C o l d ( x ′ ) C_{old}(x') Cold​(x′)为 x ′ x' x′更新前的cost-to-come值, C n e w ( x ′ ) C_{new}(x') Cnew​(x′)为 x ′ x' x′更新后的cost-to-come值, F o l d ( x ′ ) F_{old}(x') Fold​(x′)为 x ′ x' x′的更新前的父节点, F n e w ( x ′ ) F_{new}(x') Fnew​(x′)为 x ′ x' x′为更新后的父节点, l ( x , x ′ ) > 0 l(x, x') > 0 l(x,x′)>0为 x x x到 x ′ x' x′的连接权值。

结论:针对单源非负权边图最短路径问题,Dijkstra算法的结果是各状态的最优cost-to-come。

最优性证明(归纳法+反证法)

当 n = 1 n=1 n=1,由于 C ∗ ( x I ) = C ( x I ) = 0 C^*(x_I)=C(x_I)=0 C∗(xI​)=C(xI​)=0(由于边权值不能为负,可以肯定0为最小的cost-to-come值), C ( x ≠ x I ) = ∞ C(x\neq x_{I})=\infin C(x=xI​)=∞,则 x I x_{I} xI​为当前探索的状态节点, x ′ x' x′为 x I x_I xI​的后继状态节点,更新 x ′ x' x′的cost-to-come值与父节点为 < C ( x I ) + l ( x I , x ′ ) , x I > <C(x_I)+l(x_I, x'), x_I> <C(xI​)+l(xI​,x′),xI​>,选择除了 x I x_I xI​外cost-to-come值最小的状态节点作为新的探索状态节点,易得新的探索状态节点是从 x I x_I xI​到其边权值最小的后继状态节点,标记为 x m i n ′ x'_{min} xmin′​。若 C ( x m i n ′ ) ≠ C ∗ ( x m i n ′ ) C(x'_{min})\neq C^*(x'_{min}) C(xmin′​)=C∗(xmin′​),则说明存在经过 x I x_I xI​其他后继状态节点再到达 x m i n ′ x'_{min} xmin′​的更短路径。若存在这样的更短路径,则 C ( x ′ ≠ x m i n ′ ) + l < C ( x m i n ′ ) C(x'\neq x'_{min})+l<C(x'_{min}) C(x′=xmin′​)+l<C(xmin′​)。其中, l l l表示该最短路径从其他后继节点到 x m i n ′ x'_{min} xmin′​的路径长度,由于边权值非负,则 l ≥ 0 l \geq 0 l≥0,从而得 C ( x ′ ≠ x m i n ′ ) < C ( x m i n ′ ) C(x'\neq x'_{min})<C(x'_{min}) C(x′=xmin′​)<C(xmin′​),与 x m i n ′ x'_{min} xmin′​的cost-to-come值最小矛盾,故 C ( x m i n ′ ) = C ∗ ( x m i n ′ ) C(x'_{min})= C^*(x'_{min}) C(xmin′​)=C∗(xmin′​)。此步证明示意图如下:

假定从 n = 2 n=2 n=2到 n = k − 1 n=k-1 n=k−1步,每一步未探索状态节点中cost-to-come值最小的状态节点的 C = C ∗ C=C^* C=C∗;当 n = k n=k n=k时,此时未探索状态节点中cost-to-come值最小的状态节点标记为 x m i n k x^{k}_{min} xmink​,只需要证明 C ∗ ( x m i n k ) = C ( x m i n k ) C^*(x^{k}_{min})=C(x^{k}_{min}) C∗(xmink​)=C(xmink​)。
首先定义最优父节点: x x x是最短路径上的一个状态节点, x ′ x' x′是沿着最短路径方向 x x x的下一下状态节点,则 x x x是 x ′ x' x′的最优父节点。
x m i n k x^k_{min} xmink​的最优父节点有两种情况:1)最优父节点为已探索的状态节点;2)最优父节点为未探索的状态节点。
对于第一种可能(直接证明):最优父节点为已探索的状态节点,说明 x m i n k x^k_{min} xmink​是该已探索状态节点的后继节点,也即在对该已探索状态节点进行探索时,已经更新了 x m i n k x^k_{min} xmink​的cost-to-come值且能保证它是全局小的(最优父节点的定义,并且有前提:这个已探索的最优父节点本身满足 C = C ∗ C=C^* C=C∗),这个最优父节点便一直稳坐 x m i n k x^k_{min} xmink​父亲大人的位置,也即 C ∗ ( x m i n k ) = C ( x m i n k ) C^*(x^{k}_{min})= C(x^{k}_{min}) C∗(xmink​)=C(xmink​);
对于第二种可能(采用反证法):若 C ∗ ( x m i n k ) ≠ C ( x m i n k ) C^*(x^{k}_{min})\neq C(x^{k}_{min}) C∗(xmink​)=C(xmink​)且最优父节点为未探索的状态节点,从 x I x_I xI​开始沿着最短路径遍历,记录最后一个访问的已探索状态节点,记为 x j x_j xj​,则 C ∗ ( x m i n k ) = C ∗ ( x j ) + l 1 + l 2 < C ( x m i n k ) ⇒ C ∗ ( x j ) + l 1 < C ( x m i n k ) C^*(x^k_{min})=C^*(x_j)+l_1+l_2 < C(x^k_{min})\Rightarrow C^*(x_j)+l_1 < C(x^k_{min}) C∗(xmink​)=C∗(xj​)+l1​+l2​<C(xmink​)⇒C∗(xj​)+l1​<C(xmink​),其中, l 1 ≥ 0 l_1\geq 0 l1​≥0为 x j x_j xj​到 x m i n k x^k_{min} xmink​最优父节点的最短路径, l 2 ≥ 0 l_2 \geq 0 l2​≥0为最优父节点到 x m i n k x^k_{min} xmink​的非负边权值。这说明 x j x_j xj​肯定存在一个未探索的后继状态节点 x j ′ x'_j xj′​,且 C ( x j ′ ) < C ( x m i n k ) C(x'_j)<C(x^k_{min}) C(xj′​)<C(xmink​),这与前提矛盾。第二种可能的证明示意图如下:

综上可知,针对单源非负权边图最短路径问题,Dijkstra算法的结果是各状态的最优cost-to-come。

Q.E.D

三、Dijkstra算法伪代码

下图给出Dijkstra算法的伪代码,第1-4行是算法的初始化阶段,主要包括初始化起始状态节点 x I x_I xI​的cost-to-come值: C ( x I ) = 0 C(x_I)=0 C(xI​)=0,以及其它状态节点的cost-to-come值: C ( x ≠ x I ) = ∞ C(x\neq x_{I})=\infin C(x=xI​)=∞,用于存储已探索节点的集合: S = ∅ S=\emptyset S=∅,用于存储所有已探索节点的未被探索的后继节点: Q = { x I } Q=\{x_I\} Q={xI​}。

第5行是Dijkstra算法的查找cost-to-come值最小的未探索的状态节点,这一步如何实现,对算法的时间复杂度影响很大(推排序5是一个好的选择)。
第6-10行则是对当前 C C C值最小的未探索状态节点进行探索,也即遍历其未被探索的后继节点,并按条件对进 C C C值与父母节点( P P P)进行更新。

在经过Dijkstra算法后,我们得到 G G G中所有状态节点到起始节点 x I x_I xI​的最短路径,算法2给出提取最短路径的伪代码。

四、示例代码演示(python|Jupyter notebook)

本节利用两个例子来展示Dijkstra算法的效果。第一个例子也是Dijkstra算法最初的应用领域——图论;第二个例子则是基于栅格地图的自主移动机器人路径规划。其中,栅格地图可以等效成一种特殊的图数据结构。两个例子中都使用了[堆排序5](https://blog.csdn.net/qq_28063811/article/details/93034625/) 技术来实现找到未探索节点中cost-to-come值最小的节点

在代码实现中,采用一个被命名为Open_set的字典(详见python变量类型)存储待探索的节点,用字典Closed_set存储已探索的节点。其中,用堆排序(python heapq库)与被命名为Q的列表配合Open_set完成查找cost-to-come值最小的未探索节点。

4.1 图(graph)

4.1.1 图数据结构

class Edge:def __init__(self, source, destine, weight):self.weight = weightself.source = sourceself.destine = destineclass Graph:def __init__(self):self.vertices = set([])self.adjacents = {}self.inf = 99999.def add_startnode(self, vertice):self.vertices.add(vertice)self.adjacents[vertice] = {}def add_edge(self, edge):self.vertices.add(edge.source)self.vertices.add(edge.destine)if edge.source not in self.adjacents.keys():self.adjacents[edge.source] = {}self.adjacents[edge.source][edge.destine] = edge.weightif edge.destine not in self.adjacents.keys():self.adjacents[edge.destine] = {}self.adjacents[edge.destine][edge.source] = edge.weightelse:self.adjacents[edge.destine][edge.source] = edge.weightelse:self.adjacents[edge.source][edge.destine] = edge.weightif edge.destine not in self.adjacents.keys():self.adjacents[edge.destine] = {}self.adjacents[edge.destine][edge.source] = edge.weightelse:self.adjacents[edge.destine][edge.source] = edge.weight# print("add edge from {} to {}, weight {}".format(edge.source, edge.destine, edge.weight))def delete_edge(self, source, destine):if source not in self.vertices or destine not in self.vertices:return Falseif destine in self.adjacents[source].keys():self.adjacents[source].pop(destine)            if source in self.adjacents[destine].keys():self.adjacents[destine].pop(source)  def get_adjacents(self, vertex):# print("get the adjacent vertices of vertex {}".format(vertex))if vertex not in self.adjacents.keys():return set([])return self.adjacents[vertex]def vertex_number(self):return len(self.vertices)def printgraph(self):for d in self.adjacents.keys():print("%d :"% d)for b in self.adjacents[d].keys():print(d, b, self.adjacents[d][b])   def get_adjacents(self):return self.adjacents

4.1.2 Dijkstra算法与最短路径提取

import heapq
class Node:def __init__(self, C=0.0, P=None):self.C = Cself.P = Pdef Dijkstra(G, start):Closed_set, Open_set = {}, {}Open_set[start] = Node()Q = []heapq.heappush(Q, (0.0, start))print(Q)while True:if not Open_set:print("Dijkstra Algorithm Finished.")return Closed_setC, u = heapq.heappop(Q)if u in Open_set:current = Open_set.pop(u)Closed_set[u] = currentelse:continuefor v in G.adjacents[u].keys():if v not in Closed_set:v_temp_C = C + G.adjacents[u][v]if v not in Open_set:Open_set[v] = Node(v_temp_C, u)heapq.heappush(Q, (v_temp_C, v))elif Open_set[v].C > C + G.adjacents[u][v]:Open_set[v].C = v_temp_COpen_set[v].P = uheapq.heappush(Q, (v_temp_C, v))def ShortestPathFinder(Closed_set, start, end):SPath = [end]SPath_dist = Closed_set[end].Cv = Closed_set[end].Pwhile v != None:SPath.append(v)v = Closed_set[v].Pprint(v)return list(reversed(SPath)), SPath_dist

4.1.3 运行结果

如下图所示,一个非负权边无向图,该图共有13个状态节点,用正整数对它们进行编号:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],或节点之间的连接关系,以及连接权重见下图。

G = Graph()
gmat = {}
gmat[0] = {1:4, 2:5}
gmat[1] = {0:4, 3:2, 4:3, 5:6}
gmat[2] = {0:5, 4:8, 5:7, 6:7}
gmat[3] = {1:2, 7:5, 8:8}
gmat[4] = {1:3, 2:8, 7:4, 8:5}
gmat[5] = {1:6, 2:7, 8:3, 9:4}
gmat[6] = {2:7, 8:8, 9:4}
gmat[7] = {3:5, 4:4, 10:3, 11:5}
gmat[8] = {3:8, 4:5, 5:4, 6:8, 10:6, 11:2}
gmat[9] = {5:4, 6:4, 10:1, 11:3}
gmat[10] = {7:3, 8:6, 9:1, 12:4}
gmat[11] = {7:5, 8:2, 9:3, 12:3}
gmat[12] = {10:4, 11:3}
for key_i in gmat.keys():for key_j in gmat[key_i].keys():e = Edge(key_i, key_j, gmat[key_i][key_j])G.add_edge(e)%time Closed_set = Dijkstra(G, 0)
print(ShortestPathFinder(Closed_set, 0, 12))

输出结果如下:

4.2 栅格地图

在栅格地图中,每个栅格存储不同的值表示该栅格是否被障碍物占据,此时Dijkstra算法的任务是给出某一事先指定起始位置到任意非障碍物栅格的最短无碰障路径。

直接给出代码,在Jupyter notebook下可以直接运行,同步动态显示节点探索情况:

import heapq
import math
import matplotlib.pyplot as plt
import numpy as np
import math
import matplotlib
%matplotlib inline
#set up matplotlib
is_ipython = 'inline' in matplotlib.get_backend()
if is_ipython:from IPython import display
plt.ion()
plt.figure(figsize=(12, 12))class GridMap:def __init__(self, ox, oy, resolution):self.min_x = Noneself.min_y = Noneself.max_x = Noneself.max_y = Noneself.x_width = Noneself.y_width = Noneself.obstacle_map = Noneself.resolution = resolutionself.calc_obstacle_map(ox, oy)def calc_obstacle_map(self, ox, oy):self.min_x = round(min(ox))self.min_y = round(min(oy))self.max_x = round(max(ox))self.max_y = round(max(oy))print("min_x:", self.min_x)print("min_y:", self.min_y)print("max_x:", self.max_x)print("max_y:", self.max_y)self.x_width = round((self.max_x - self.min_x) / self.resolution)+1self.y_width = round((self.max_y - self.min_y) / self.resolution)+1print("x_width:", self.x_width)print("y_width:", self.y_width)# obstacle map generationself.obstacle_map = np.zeros((self.x_width, self.y_width))for x, y in zip(ox, oy):ix, iy = self.calc_xy_index(x, self.min_x), self.calc_xy_index(y, self.min_y)if 0<=ix<self.x_width and 0<=iy<self.y_width:self.obstacle_map[ix, iy] = 10.0  def calc_position(self, index, minp):pos = index * self.resolution + minpreturn posdef calc_xy_index(self, position, minp):return round((position - minp) / self.resolution)def calc_unique_index(self, node):return node.iy*self.x_width + node.ixdef verify_node(self, node):if node.ix >= self.x_width or node.ix < 0 or node.iy >= self.y_width or node.iy < 0:return Falseif abs(self.obstacle_map[node.ix, node.iy] - 10.0) < 0.01:return False        return Truesx = -5.0  # [m]
sy = -5.0  # [m]
gx = 50.0  # [m]
gy = 50.0  # [m]
grid_size = 5.0  # [m]
robot_radius = 1.0  # [m]# set obstacle positions
ox, oy = [], []
for i in range(-10, 60):ox.append(i)oy.append(-10.0)
for i in range(-10, 60):ox.append(60.0)oy.append(i)
for i in range(-10, 61):ox.append(i)oy.append(60.0)
for i in range(-10, 61):ox.append(-10.0)oy.append(i)
for i in range(-10, 40):ox.append(20.0)oy.append(i)
for i in range(0, 40):ox.append(40.0)oy.append(60.0 - i)
grid_map = GridMap(ox, oy, grid_size)class Node:def __init__(self, ix, iy, C=0.0, P=None):self.ix = ixself.iy = iyself.C = Cself.P = Paction_set = {'UP':[0, 1], 'DOWN':[0, -1], 'LEFT':[-1, 0], 'RIGHT':[1, 0]}def get_neighbors(current, grid_map):for a in action_set:node = Node(current.ix+action_set[a][0], current.iy+action_set[a][1], current.C+1.0, grid_map.calc_unique_index(current))if grid_map.verify_node(node):yield nodedef Dijkstra(grid_map, start_node):Closed_set, Open_set = {}, {}Open_set[grid_map.calc_unique_index(start_node)] = start_nodeQ = []heapq.heappush(Q, (0.0, grid_map.calc_unique_index(start_node)))while True:if not Open_set:print("Dijkstra Algorithm Finished.")return Closed_setC, c_id = heapq.heappop(Q)if c_id in Open_set:current = Open_set.pop(c_id)Closed_set[c_id] = currentelse:continuefor node in get_neighbors(current, grid_map):neighbor_index = grid_map.calc_unique_index(node)if neighbor_index in Closed_set: continueif neighbor_index not in Open_set or Open_set[neighbor_index].C > node.C:Open_set[neighbor_index] = nodeheapq.heappush(Q, (node.C, neighbor_index))grid_map.obstacle_map[node.ix, node.iy] = 2plt.clf()plt.imshow(grid_map.obstacle_map)if is_ipython:display.clear_output(wait=True)display.display(plt.gcf())        def ShortestPathFinder(grid_map, Closed_set, end_node):if grid_map.verify_node(end_node) == False:print("the goal is unreachable!!!please change a reachable goal...")return [], [], -1end_index = grid_map.calc_unique_index(end_node)SPath_x = [Closed_set[end_index].ix]SPath_y = [Closed_set[end_index].iy]      SPath_dist = Closed_set[end_index].C*grid_map.resolutionv_index = Closed_set[end_index].Pwhile v_index: SPath_x.append(Closed_set[v_index].ix)SPath_y.append(Closed_set[v_index].iy)v_index = Closed_set[v_index].Preturn list(reversed(SPath_x)), list(reversed(SPath_y)), SPath_diststart_node = Node(2, 2)
end_node = Node(12, 12)
grid_map.obstacle_map[start_node.ix, start_node.iy] = 8plt.imshow(grid_map.obstacle_map)Closed_set = Dijkstra(grid_map, start_node)
path_x, path_y, path_dist = ShortestPathFinder(grid_map, Closed_set, end_node)
grid_map.obstacle_map[end_node.ix, end_node.iy] = 4
for ix, iy in zip(path_x, path_y):grid_map.obstacle_map[ix, iy] = 4plt.clf()
plt.imshow(grid_map.obstacle_map)
if is_ipython:display.clear_output(wait=True)display.display(plt.gcf())

运行过程如以下动态图(python动态演示技术请看本人博客6所示:
第一步:Dijkstra算法得到从起点到所有其他节点的最短路径;
第二步:任意指定目标点,利用ShortestPathFinder函数提取从起点到该目标点的最短路径。

五、重要知识点

  • Dijkstra是针对单源非负权边图最短路径问题,并且得到任意节点到指定起点的最短路径(如果存在的话);
  • Dijkstra基本思想是采用cost-to-come值作为贪心策略,但是能够保证全局最优;
  • 采用推排序实现查找未探索节点中cost-to-come值。

六、总结

Dijkstra算法作为最典型的寻路算法,能够得出最短路径,在很多专业课程中都能看到它的身影,如数据结构、图论、运筹学等等 。它能够用来求解任何能够抽象成非负权值图(graph)的问题,例如:旅行商(Travelling Salesman Problem, TSP)问题,自主移动机器人路径规划问题。

本文从Dijkstra提出的背景、算法原理到最后的代码演示,全方位的对Dijkstra算法进行了介绍。Dijkstra的优点是能保证全局最优(如果存在的话),并且算法运行完成直接可以得到所有节点到指定起点的最短路径。这种做法有一定的实际用处,例如我们想做一个到指定起点的最短路径知识库,选Dijkstra算法准没错。

实际任务的起点、环境(图的连接关系)以及目标点都在动态变化,这样一次性计算的知识库也只是一次性。这个时候采用Dijkstra算法就显得有点笨重。对Dijkstra算法稍加修改,它能退化为一个指定起点与终点的单目标版本,但是在这个新增加的目标点约束下,直觉上整个任务是增加某些可用的新信息,那么是否可以将这些新信息加入到Dijkstra的贪心策略中,以提高寻路的效率呢?

在下一篇我们将要介绍A*算法中能找到答案。

七、思考 (欢迎在博客下方留言)

  • 如何在对Dijkstra算法稍加修改,使其变成针对特定起点到特定终点的最短路径问题?
  • 不改动Dijkstra算法(包括ShortestPathFinder函数),能否输出任意起点到特定终点的最短路径?
  • 指定起点,Dijkstra算法定义了cost-to-come值作为贪婪策略,那么在指定起点的同时还指定终点,能否偿试定义一个类似的cost-to-go值?

参考文献


  1. Dijkstra, E. W. A note on two problems in connexion with graphs (PDF). Numerische Mathematik, 1959, 23(3): 269-271. ↩︎

  2. Edsger W. Dijkstra (web). wikipedia, 2022. ↩︎

  3. 罗勇军. Edsger W. Dijkstra – 巨人的肩膀(web). CSDN, 2022. ↩︎

  4. 王选. 从Dijkstra谈帅才的洞察力(web). 计算机世界, 2002. ↩︎

  5. guanlovean. 堆排序(web). CSDN, 2019. ↩︎ ↩︎

  6. windSeS. python中plot实现即时数据动态显示方法(web). CSDN, 2022. ↩︎

基于图搜索的规划算法之Dijkstra相关推荐

  1. 基于图搜索的规划算法之Voronoi图法

    文章目录 一.维诺图的定义 二.基于Fortune的平面扫描法 2.1 原理 2.2 算法 2.3 伪代码 2.4 C++版程序 三.将感知结果转换为维诺图 3.1 感知结果的一般表示(复习) 3.2 ...

  2. 基于采样的规划算法之RRT家族(二):RRT-Connect

    RRT家族和 A* 家族的共性是:都是生成一棵不断靠近目标的路径树.只是生成的方式不同,RRT家族通过随机采样结点来拓展路径树, A* 家族则基于贪心策略在规划问题图(graph)上搜索叶点来拓展路径 ...

  3. 基于采样的规划算法之概率路图(PRM)法

    概率路图(Probabilistic Road Map, PRM)法严格意义上只是一种构建规划问题图(graph)的方法,在目的上与前面介绍的可视图.Voronoi图一样.在构建完规划问题图(grap ...

  4. 基于采样的规划算法之RRT家族(六):总结

    从RRT到时空RRT,我们介绍了总共5种RRT家族算法.RRT-Connect.RRT* .Informed RRT* 都是为了让原始RRT算法花更小的时间找到更短的路径.最后一个时空RRT算法则是为 ...

  5. 基于非线规划算法的船舶能量调度

    拟开展能量调度小论文学习分享心得系列文章 1.基于非线规划算法的船舶能量调度 拟开展能量调度小论文学习分享心得系列文章 前言 一.解决方案 二.仿真实验: 三.总结: 前言 在使用深度强化学习算法(D ...

  6. Robotics: Computational Motion Planning(路径规划)笔记(一):基于图搜索的方法-Grassfire、Dijkstra和A*算法

    在本课程中,我们将考虑机器人如何决定如何到达目标的问题.这个问题通常被称为运动规划,它以不同的方式来描述不同的情况.您将学习一些解决这个问题的最常用方法,包括基于图的方法.基于采样的方法和人工势场法. ...

  7. 路径规划之基于插值的规划算法

    关注同名微信公众号"混沌无形",有趣好文! 原文链接:机器人曲线插值拟合算法研究现状简述(包含原文PDF百度云下载链接) 第一类是采用多项式曲线来描述机器人运动轨迹,这是因为多项式 ...

  8. 路径规划之基于优化的规划算法

    关注同名微信公众号"混沌无形",有趣好文! 原文链接:机器人曲线插值拟合算法研究现状简述(包含原文PDF百度云下载链接) 第三类算法是主要是将多目标多变量多约束耦合的规划模型转化为 ...

  9. 基于采样的规划算法之RRT家族(三):RRT*

    RRT算法是边随机产生节点边生长一棵路径树,当这棵树与目标点相遇,便从相遇点回溯到起点得到路径解.对于新产生的随机节点,原始RRT算法将路径树上距离它最近的节点作为它的父节点,并不能保证新节点通过该父 ...

最新文章

  1. 局部内部类访问方法中的局部变量为什么加final
  2. JAVA各种并发锁从synchronized 到CAS 到 AQS
  3. golang strings包部分函数使用
  4. java map set_java中Map、Set、List的简单使用教程(快速入门)
  5. 《JavaScript高级程序设计》阅读笔记(二十一):JavaScript中的XML
  6. 中国WEB 2.0的质变过程
  7. php快速排序法远离,php快速排序原理与实现方法分析
  8. idea 找不到或无法加载主类_解决IDEA中Groovy项目no Groovy library is defined的问题
  9. 十六进制除法运算法则_苏教版数学七年级上册 微课视频 2.6 有理数的乘法与除法(1)...
  10. ajax post json php,ajax POST json对象给PHP,PHP怎么接收值
  11. 分布式事务的特征、原理、以及常见3种解决方案
  12. mongodb 备份压缩_MongoDB-备份和恢复
  13. 轻松搞定 Shell 玩转 HiveSQL
  14. bs4用法实例:用Python爬取金融市场数据
  15. K3默认序时簿是不体现即时库存的,如果需要在序时簿将物料的即时库存数据带入,可以按照下方的步骤实现:
  16. 50套电子看板,数据大屏设计,数据展示模板,大屏可视化,大数据分析平台,ui设计模板
  17. cv python 读取灰度图
  18. error: C++ requires a type specifier for all declarations
  19. 打造铜墙铁壁 DDoS攻击全面解析
  20. 免费idc公益接口_数据科学促进社会公益免费开放数据的最佳来源

热门文章

  1. 如何免费在线把Figma转成Sketch
  2. 计算机高速缓冲存储器工作原理,高速缓冲存储器部件结构及原理解析
  3. 摘抄自别人的人生感悟
  4. tomcat启动不报错但不加载war
  5. 时间不一定能证明许多东西,但一定会让你看透许多东西。
  6. java中setattr功能_hasattr()、getattr()、setattr()函数的使用
  7. CGB2103-day06
  8. Mac应用Drone Station结合普通游戏手柄让AR Drone飞起来
  9. 简单说说对QT中moveToThread实现多线程操作的理解
  10. 单元测试(哈酷酷么塔塔)