资讯专栏INFORMATION COLUMN

对狄克斯特拉算法的理解

chuyao / 1163人阅读

摘要:致敬首先向伟大的牛人致敬使用狄克斯特拉算法如果所示找出从起点到终点耗时最短的路径。狄克斯特拉算法思路步骤或思路如下找出最便宜的节点,即可在最短时间内前往的节点。

狄克斯特拉算法是一种实现了在有障碍物的两个地点之间找出一条最短路径的高效算法,解决了机器人学中的一个十分关键的问题,即运动路径规划问题,至今仍被广泛应用。是“贪心方法(greedy method)”的一个成功范例。
致敬

首先向伟大的牛人致敬!

使用狄克斯特拉算法

如果所示:

找出从起点到终点耗时最短的路径。其中,每个数字表示的都是时间,单位分钟。线路使用有方向的箭头线路表示,表示只有一个方向可以使用,统称有向图

常规思路

为了找到从起点到终点耗时最短的路径,我们通常需要找出从起点到终点所有的路径,然后进行一一对比。

路径一

路径一是从起点出发,经过 A 节点,到达终点,共计用时 6 + 1 = 7 分钟。

路径二

路径二是从起点出发,经过 B 节点,到达终点,共计用时 2 + 5 = 7 分钟。

路径三

路径三从起点出发,经过 B 节点,再经过 A 节点,到达终点,共计用时 2 + 3 + 1 = 6 分钟。

综上,我们已经穷举完了所有可能的路径,不可能再找出另外的路径了。同时,对比一下三种路径,你会发现路径三用时最短,只需要 6 分钟。呵呵,so easy,妈妈再也不用担心我的学习了。既然,人可以做出结果,那么计算机利用此种方法,也就是所谓的穷举法,当然也能找到最优路径。

不过,别得意,你的妈妈还得担心你的学习。如果路途中,不止 A、B 两个节点,而是接近无穷多个节点,记住是接近无穷多个节点,......懵逼从天空飘过。

此时,肯定有同学会跳出来反对,无穷多个节点,这就是无限。无限,也就是无界,就是死循环的问题了,肯定无法得到答案,此题出得有问题。

这个问题提得好,必须有界才能有答案,该问题是接近无限多,也就是个很大很大的边界,是超出了人力范围的边界。......懵逼继续从天空飘过。 但是,计算机肯定是能够计算出有界的问题的,利用穷举法当然可以算出,不过这里又产生一个问题,穷举法是检索每条可能的路径,这肯定会消耗很大的计算机运算能力,那么有没有更优的方法,至少不用穷举出所有路径的方法呢?当然,有那么多的牛人供我们致敬,答案是肯定的。

狄克斯特拉算法思路

步骤或思路如下:

找出最便宜的节点,即可在最短时间内前往的节点。

对于该节点的所有邻居,检查是否有前往它们的更短路径,如果有,就更新其开销。

处理过的节点,进行标记,以后将不再处理。

重复以上过程,直到对图中的每个节点都这样做了。

计算出最终路径。

第一步:找出最便宜的节点。你站在起点,不知道该前往节点 A 还是节点 B ,茫然无措ing........。此时,散列表可以派上用场了。啥是散列表?你可以把它当做是一个列表,详细的东西问谷歌,请自备梯子

前往节点 A 需要6 分钟,而前往节点 B 需要 2 分钟。至于前往其它节点,你还不知道需要多长时间。那么散列表如下:

父节点 节点 耗时
起点 A 6
起点 B 2
起点 终点

第二步:由于从起到到节点 B 的路径耗时少,先计算经节点 B 前往其各个邻居所需的时间。

父节点 节点 耗时
B A 5 更新耗时
- B 2
B 终点 7 更新耗时

这一步,找到了一条前往节点 A 的更短路径!直接前往节点 A 需要 6 分钟。但是经过节点 B 前往节点 A 只需要 5 分钟。

对于节点 B 的邻居,如果找到前往它的更短路径,就更新其开销。

前往节点 A 的更短路径,时间从 6 分钟缩短到 5 分钟。

前往终点的更短路径,时间从无穷大缩短到 7 分钟。

第三步:对节点 B 已进行处理,所以要对节点 B 进行标记,以后将不再处理节点 B。

第四部: 重复!

重复第一步:找出可在最短时间内前往的节点。除节点 B 之外,可以在最短时间内前往的节点是节点 A 。
重复第二步:更新节点 A 的所有邻居的开销。

父节点 节点 耗时
- A 5 已是最小耗时,无需更新
- B 2
A 终点 6 更新耗时

现在对每个节点都运行了狄克斯特拉算法(无需对终点这样做)。现在,你知道:

前往节点 B 需要 2 分钟;

前往节点A 需要 5 分钟;

前往终点需要 6 分钟。

所以你会发现,前往终点的时间为 6 分钟!!!

Python代码实现 实现一个能够找出开销最低节点的函数
def find_lowest_cost_node(costs):
    lowest_cost = float("inf") # 设置初始开销为无穷大,因为你现在很茫然
    lowest_cost_node = None # 设置初始最低开销节点为 None
    for node in costs: # 遍历所有的节点
        cost = costs[node]
        if cost < lowest_cost and node not in processed: # 如果当前节点的开销更低且未处理过,
            lowest_cost = cost # 就将其视为开销最低的节点。
            lowest_cost_node = node # 最低开销节点为当前的节点。
    return lowest_cost_node
创建用于存储所有节点及其前往邻居开销的散列表代码
graph["start"] = {}
graph["start"]["a"] = 6
graph["start"]["b"] = 2

graph["a"] = {}
graph["a"]["fin"] =1

graph["b"] = {}
graph["b"]["a"] =3
graph["b"]["fin"] = 5

graph["fin"] = {} # 终点没有任何邻居

表示整个图的散列表类似下面这样。

父节点 节点 耗时
起点 A 6
起点 B 2
A 终点 1
B A 3
B 终点 5
起点 终点 -
按流程实现代码

一、创建从起点开始的开销表代码如下:

infinity = float("inf")
costs = {}
costs["a"] = 6
costs["b"] = 2
costs["fin"] = infinity

二、创建存储父节点的散列表代码如下:

parents = {}
parents["a"] = "start"
parents["b"] = "start"
parents["fin"] = None

三、创建一个数组,用于记录处理过的节点,对于同一个节点,不用多次处理。

processed = []

四、按照算法列出代码

node = find_lowest_cost_node(costs) # 在未处理的节点中找出开销最小的节点
while node is None: # 这个 while 循环在所有节点都被处理过后结束
    cost = costs[node]
    neighbors = graph[node]
    for n in neighbors.keys(): # 遍历当前节点的所有邻居
        new_cost = cost + neighbors[n]
        if costs[n] > new_cost: # 如果经当前节点前往该邻居最近,
            costs[n] = new_cost # 就更新该邻居的开销
            parents[n] = node # 同时将该邻居的父节点设置为当前节点
    processed.append(node) # 将当前借调标记为已处理
    node = find_lowest_cost_node(costs) # 找出接下来要处理的节点,并循环。
按照个人的理解创建代码

在 Python 3.5.2 上亲测有效。

# -*- coding:utf-8 -*-
__author__ = "东方鹗"
__blog__ = "www.os373.cn"

def find_lowest_cost_node(costs, processed):
    lowest_cost = float("inf") # 设置初始开销为无穷大,因为你现在很茫然
    lowest_cost_node = None # 设置初始最低开销节点为 None
    for node in costs: # 遍历所有的节点
        cost = costs[node]
        if cost < lowest_cost and node not in processed: # 如果当前节点的开销更低且未处理过,
            lowest_cost = cost # 就将其视为开销最低的节点。
            lowest_cost_node = node # 最低开销节点为当前的节点。
    return lowest_cost_node


def best_route():
    """ 存储所有节点及其下一个节点开销的字典 """
    graph = {"start": {"a": 6, "b": 2}, "a": {"fin": 1}, "b": {"a": 3, "fin": 5}, "fin": {}}

    """ 从起点开始,包含所有下一个节点开销的字典 """
    infinity = float("inf")
    costs = {"a": 6, "b": 2, "fin": infinity}

    """ 从起点开始,存储所有父节点的散列表 """

    parents = {"a": "start", "b": "start", "fin": None}
    best_route = ""
    processed = []

    node = find_lowest_cost_node(costs, processed) # 在未处理的节点中找出开销最小的节点    
    while node is not None: # 这个 while 循环在所有节点都被处理过后结束
        cost = costs[node]
        neighbors = graph[node]
        for n in neighbors.keys(): # 遍历当前节点的所有邻居
            new_cost = cost + neighbors[n]
            if costs[n] > new_cost: # 如果经当前节点前往该邻居最近,
                costs[n] = new_cost # 就更新该邻居的开销
                parents[n] = node # 同时将该邻居的父节点设置为当前节点
        processed.append(node) # 将当前借调标记为已处理
        node = find_lowest_cost_node(costs, processed) # 找出接下来要处理的节点,并循环。
    
    p = parents["fin"]
    
    while True:  
        best_route += "%s<——" % p
        p = parents[p]
        
        if p is "start":
            break               
        
                
    return "到达终点的最终路径是: 终点<——%s起点。
最快到达的时间是%s分钟" % (best_route, costs["fin"])
        
if __name__ == "__main__":
    best_route = best_route()
    print(best_route)

结果如下:

到达终点的最终路径是: 终点<——a<——b<——起点。
最快到达的时间是6分钟
狄克斯特拉算法的局限

1、该算法只适用于有向无环图!
2、该算法将 0 视作最小的权值,也就是说,如果出现负权值的情况,那么该算法将失效!

reference

《算法图解》

本人用C作的视频,敬请点阅

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/40911.html

相关文章

  • 【你该懂一点Javascript算法系列】之单源最短路径 - Dijkstra算法

    摘要:算法系列单源最短路径算法迪杰斯特拉算法是由荷兰计算机科学家狄克斯特拉于年提出的,因此又叫狄克斯特拉算法。是从一个顶点到其余各顶点的最短路径算法,解决的是有向图中最短路径问题。 Javascript算法系列 - 单源最短路径 - Dijkstra算法 迪杰斯特拉算法是由荷兰计算机科学家狄克斯特拉于1959年提出的,因此又叫狄克斯特拉算法。是从一个顶点到其余各顶点的最短路径算法,解决的是有...

    SoapEye 评论0 收藏0
  • 算法系列——JavaScript中广度优先搜索思想实现

    摘要:散列表上面的地图向我们展示了如何用广度优先搜索的思想找到北京到广州的最短路线。在广度优先搜索中,我们需要用到队列的这种思想来实现查找。建立了下面这个模型武汉广州西藏上海上海武汉广州代码完整实现,利用递归和广度优先搜索的思想实现。 什么是广度优先搜索? 如果只是是背概念,幼儿园的小朋友都能背下来念给你听。 假设看这篇文章的都和我一样是个前端工程师,我们要从广度优先搜索(BFS)中学到什么...

    everfly 评论0 收藏0
  • 寻路之 A* 搜寻算法

    摘要:但再认真想想,其实这道题目就类似我们日常用的导航,寻找起点和终点可行的最短路线。越小时,那么起点到目标点的可行路径越小。首先将起点放进中,然后搜寻起点附近可行方格放到中作记录。 showImg(https://segmentfault.com/img/remote/1460000009932413?w=660&h=440); 最近做到一道题,题目如下: 有 A、B 两点,中间有一堆障碍...

    banana_pi 评论0 收藏0
  • 漫谈 | “黎曼猜想”和区块链加密算法到底有什么关系?

    摘要:假如黎曼猜想被证实,区块链将毁灭近日,黎曼猜想四个字疯狂刷屏。黎曼猜想由数学家波恩哈德黎曼于年提出。因此,黎曼猜想一旦被证明,则意味着素数之密被解开,算法也就将被攻破了。而大多数区块链所用的加密算法不是,而是椭圆曲线加密算法。 玛丽女王的密码之生死命悬一线 showImg(https://segmentfault.com/img/bVbhD7s?w=740&h=876); 16世纪伊丽...

    tracymac7 评论0 收藏0

发表评论

0条评论

chuyao

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<