首页 > 编程语言 >Dijkstra算法详解【附算法代码与运行结果】

Dijkstra算法详解【附算法代码与运行结果】

时间:2024-09-28 15:49:26浏览次数:8  
标签:node index min self list Dijkstra 算法 详解 节点

算法背景

Dijkstra算法是一种用于在加权图中找到单个源点到所有其他顶点的最短路径的算法。这种算法由荷兰计算机科学家艾兹格·戴克斯特拉(Edsger W. Dijkstra)在1956年提出。它适用于有向图和无向图,并且图中的边权重必须是非负数。


基本原理

如下图所示,找到一条从v1(节点1)到v6(节点1)的最短路径,从起点开始逐步扩展,每一步为一个节点寻找到最短的路径。

主要是运行如下这个循环:

While True:

step1:从未访问的节点选择距离最小的节点收录(贪心思想)。

step2:收录节点后遍历该节点的邻接节点,并更新到起点的距离。

】为什么被收录的节点已经找到距离起点最短的路径?

(反证法)假设要收录上述的v4,但是有一条g(1->2->4)< g(1->4),这样就可以推出 g(1->2)< g(1->4),这样搞根据贪心思想需要收录的是v2,与假设矛盾!


算法流程

open list表示存放从当前节点到节点已经更新过的路径;

closed list表示截至目前到起点最短路径的节点进行收录。

那么针对下面这个图的存储流程应如下:

open list:1(0)  #从起点开始,1(0)表示节点1到起点的距离为0

closed list:

open list:

closed list:1(0)

open list:2(2)  4(1) # 遍历收录节点的邻接节点

closed list:1(0)

open list:2(2)  

closed list:1(0)  4(1)

open list:2(2)  3(3)  6(9)   7(5)

closed list:1(0)  4(1)

open list:3(3)  6(9)   7(5)

closed list:1(0)  4(1)  2(2)

open list:3(3)  6(9)   7(5)  5(13) #由于4号节点已经被收录已经是最优的,无需更新

closed list:1(0)  4(1)  2(2)

open list:6(9)   7(5)  5(13)

closed list:1(0)  4(1)  2(2)  3(3) #此处在遍历4号节点时,已经找到6号节点了,但是再遍历3号节点时距离为8比之前的9要短,所以更新一下

open list:6(8)   7(5)  5(13)

closed list:1(0)  4(1)  2(2)  3(3)

open list:6(8)   5(13)

closed list:1(0)  4(1)  2(2)  3(3)   7(5)

open list:6(6)   5(13)

closed list:1(0)  4(1)  2(2)  3(3)   7(5)

open list: 5(13)

closed list:1(0)  4(1)  2(2)  3(3)   7(5)  6(6)

获取遍历路径的方式:如下图,在6号节点中记录他来自7号父节点,再依次往前推,直至找到起点所设置的-1,停止,最终就能得到1->4->7->6路径。

栅格地图与节点编写方式:

机器人的运动方式:


案例实操

对起点[-5,-5],终点 [50, 50]在边长范围为[-10,60]的正方形进行路径规划,其中设置了两个墙体作为障碍物。

【实验代码】

"""

Grid based Dijkstra planning

author: Atsushi Sakai(@Atsushi_twi)

"""

import matplotlib.pyplot as plt
import math

show_animation = True


class Dijkstra:

    def __init__(self, ox, oy, resolution, robot_radius):
        """
        Initialize map for planning

        ox: x position list of Obstacles [m]
        oy: y position list of Obstacles [m]
        resolution: grid resolution [m]
        rr: robot radius[m]
        """

        self.min_x = None
        self.min_y = None
        self.max_x = None
        self.max_y = None
        self.x_width = None
        self.y_width = None
        self.obstacle_map = None

        self.resolution = resolution #resolution就是传进来的栅格大小
        self.robot_radius = robot_radius
        self.calc_obstacle_map(ox, oy)#构建栅格地图传入障碍物
        self.motion = self.get_motion_model()#定义机器人的运动方式

    class Node:#定义每一个栅格作为节点
        def __init__(self, x, y, cost, parent_index):
            self.x = x  # index of grid
            self.y = y  # 栅格坐标index of grid
            self.cost = cost  # 路径即代价值g(n)
            self.parent_index = parent_index  # 记录父节点寻找路径index of previous Node

        def __str__(self):
            return str(self.x) + "," + str(self.y) + "," + str(
                self.cost) + "," + str(self.parent_index)

    def planning(self, sx, sy, gx, gy):
        """
        dijkstra path search

        input:
            s_x: start x position [m]
            s_y: start y position [m]
            gx: goal x position [m]
            gx: goal x position [m]

        output:
            rx: x position list of the final path
            ry: y position list of the final path
        """

        #设置起点与终点,前两个需要转换,路径代价先设为0,父节点没有的
        start_node = self.Node(self.calc_xy_index(sx, self.min_x),
                               self.calc_xy_index(sy, self.min_y), 0.0, -1)   #calc_xy_index从位置转换为下标round((position - minp) / self.resolution)
        goal_node = self.Node(self.calc_xy_index(gx, self.min_x),
                              self.calc_xy_index(gy, self.min_y), 0.0, -1)

        open_set, closed_set = dict(), dict()     # 通过字典的方式key - value存储
        open_set[self.calc_index(start_node)] = start_node#calc_index为栅格编号

        while 1:#文档中的流程图
            c_id = min(open_set, key=lambda o: open_set[o].cost)  # 取cost最小的节点记为c_id
            current = open_set[c_id]# 取出里面的值

            # 动画仿真show graph
            if show_animation:  # pragma: no cover
                plt.plot(self.calc_position(current.x, self.min_x),
                         self.calc_position(current.y, self.min_y), "xc")
                # for stopping simulation with the esc key.
                plt.gcf().canvas.mpl_connect(
                    'key_release_event',
                    lambda event: [exit(0) if event.key == 'escape' else None])
                if len(closed_set.keys()) % 10 == 0:
                    plt.pause(0.001)

            # 判断是否是终点
            if current.x == goal_node.x and current.y == goal_node.y:
                print("Find goal")
                goal_node.parent_index = current.parent_index
                goal_node.cost = current.cost
                break

            # 从open_set中删掉
            del open_set[c_id]

            # 添加至 closed_set
            closed_set[c_id] = current

            # 通过机器人的运动方式遍历邻接节点 expand search grid based on motion model
            for move_x, move_y, move_cost in self.motion:
                node = self.Node(current.x + move_x,
                                 current.y + move_y,
                                 current.cost + move_cost, c_id)#根据当前节点的xy加上运动方式的xy,当前的路径加上移动的代价求邻接节点
                n_id = self.calc_index(node)#当前节点key的顺序,用来找路径

                if n_id in closed_set:#判断当前节点key是否已经收录到closed_set,如若是,continue进入下一个循,若没有收录需要对值进行计算收录
                    continue

                if not self.verify_node(node):#判断这个邻接节点是否超出地图范围或者是障碍物,如若是,continue进入下一个循
                    continue

                if n_id not in open_set:
                    open_set[n_id] = node  #若没有收录需要对值进行计算收录 Discover a new node
                else:
                    if open_set[n_id].cost >= node.cost:
                        #如果已经在open_set里面比较值大小进行,若小更新
                        open_set[n_id] = node

        rx, ry = self.calc_final_path(goal_node, closed_set)#计算最后的路径

        return rx, ry

    def calc_final_path(self, goal_node, closed_set):
        # generate final course
        rx, ry = [self.calc_position(goal_node.x, self.min_x)], [
            self.calc_position(goal_node.y, self.min_y)]#代码中的rx, ry两个列表存储路径,为了画图方便,存贮具体的数值而非下标
        parent_index = goal_node.parent_index#从中带你的父节点开始
        while parent_index != -1:
            n = closed_set[parent_index]
            rx.append(self.calc_position(n.x, self.min_x))
            ry.append(self.calc_position(n.y, self.min_y))#从父节点的索引就得到他来自那一个节点放入rx,ry列表中
            parent_index = n.parent_index#再把这个n节点的取出来循环到起点初始化的-1

        return rx, ry

    def calc_position(self, index, minp):
        pos = index * self.resolution + minp#将下标与栅格大小相乘加上最小值的偏移量
        return pos

    def calc_xy_index(self, position, minp):
        return round((position - minp) / self.resolution)

    def calc_index(self, node):
        return node.y * self.x_width + node.x #y乘x轴个数再加上x值的到该格编号

    def verify_node(self, node):
        px = self.calc_position(node.x, self.min_x)
        py = self.calc_position(node.y, self.min_y)

        if px < self.min_x:
            return False
        if py < self.min_y:
            return False
        if px >= self.max_x:
            return False
        if py >= self.max_y:
            return False

        if self.obstacle_map[node.x][node.y]:
            return False

        return True

    def calc_obstacle_map(self, ox, oy):
        ''' 第1步:构建栅格地图 '''
        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)
        self.y_width = round((self.max_y - self.min_y) / self.resolution)#计算x轴与y轴方向具体的栅格个数x_width y_width,(最大-最小)/栅格大小
        print("x_width:", self.x_width)
        print("y_width:", self.y_width)

        # obstacle map generation
        # 初始化地图
        self.obstacle_map = [[False for _ in range(self.y_width)]
                             for _ in range(self.x_width)]#地图采用二维向量表示,在代码中用两层的列表表示,false表示还没设置障碍物
        # 设置障碍物
        for ix in range(self.x_width):#前两层for循环就是用来遍历栅格的
            x = self.calc_position(ix, self.min_x)#通过下标来计算位置
            for iy in range(self.y_width):
                y = self.calc_position(iy, self.min_y)
                for iox, ioy in zip(ox, oy):#第三层for循环用来遍历障碍物
                    d = math.hypot(iox - x, ioy - y)#先是计算障碍物到栅格的距离
                    if d <= self.robot_radius:#膨胀障碍物构建 Configuration Space 构型空间的过程,如果距离比机器人的半径要小,机器人不能通行,把这一栅格设置为true
                        self.obstacle_map[ix][iy] = True
                        break

    @staticmethod#定义机器人的运动方式
    def get_motion_model():
        # dx, dy, cost
        motion = [[1, 0, 1],#前两个数表示机器人的移动方向,第三个数表示移动距离即代价
                  [0, 1, 1],
                  [-1, 0, 1],
                  [0, -1, 1],
                  [-1, -1, math.sqrt(2)],
                  [-1, 1, math.sqrt(2)],
                  [1, -1, math.sqrt(2)],
                  [1, 1, math.sqrt(2)]]

        return motion

def main():
    # 设置起点与终点start and goal position
    sx = -5.0  # [m]
    sy = -5.0  # [m]
    gx = 50.0  # [m]
    gy = 50.0  # [m]
    grid_size = 2.0  # 设置栅格大小 [m]
    robot_radius = 1.0  # 设置机器人半径 [m]

    # 设置障碍物位置,图中所有黑点,前四个for循环设置围成的四堵墙,后面的两个foe循环设置中间的两个,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)

    if show_animation:  #画上面这些图 pragma: no cover
        plt.plot(ox, oy, ".k")
        plt.plot(sx, sy, "og")
        plt.plot(gx, gy, "xb")
        plt.grid(True)
        plt.axis("equal")

    dijkstra = Dijkstra(ox, oy, grid_size, robot_radius)
    rx, ry = dijkstra.planning(sx, sy, gx, gy)

    if show_animation:  # pragma: no cover
        plt.plot(rx, ry, "-r")
        plt.pause(0.01)
        plt.show()

if __name__ == '__main__':
    main()

【运行结果】

标签:node,index,min,self,list,Dijkstra,算法,详解,节点
From: https://blog.csdn.net/ZW__wd/article/details/142616795

相关文章

  • 代码随想录算法训练营第三天 | 熟悉链表
    链表的存储方式数组是在内存中是连续分布的,但是链表在内存中可不是连续分布的。链表是通过指针域的指针链接在内存中各个节点。所以链表中的节点在内存中不是连续分布的,而是散乱分布在内存中的某地址上,分配机制取决于操作系统的内存管理。链表的定义template<typenameT>......
  • 【机器学习】ID3、C4.5、CART 算法
    目录常见的决策树算法1.ID32.C4.53.CART决策树的优缺点优点:缺点:决策树的优化常见的决策树算法1.ID3ID3(IterativeDichotomiser3)算法使用信息增益作为特征选择的标准。它是一种贪心算法,信息增益表示按某特征划分数据集前后信息熵的变化量,变化量越大,表示使用该......
  • 9--苍穹外卖-SpringBoot项目中Redis的介绍及其使用实例 详解
    目录Redis入门Redis简介Redis服务启动与停止服务启动命令Redis数据类型5种常用数据类型介绍各种数据类型的特点Redis常用命令字符串操作命令哈希操作命令列表操作命令集合操作命令有序集合操作命令通用命令在java中操作RedisRedis的Java客户端SpringDataRe......
  • 各种CLOCK算法
    这篇文章的背景是操作系统里的页缓冲。LRU要求每次访问某个页的时候都进入trap,由操作系统把这个页放到栈顶。显然这是不能接受的。所以一般采用CLOCK算法或者其变种,其特点是在hit的时候,只需要由硬件执行一个很简单的操作(通常是设置访问位),把这次访问记录下来即可。然后在合适的时......
  • 垃圾回收算法
    垃圾回收算法分为跟踪式垃圾回收(Tracinggarbagecollection)和引用计数(Referencecounting)两大类。跟踪式垃圾回收跟踪式垃圾回收的基本原理是先认定一些对象为root,比如全局变量和栈变量。然后跟踪(trace)哪些对象是从这些root可达的,而剩下的从这些root不可达的对象就是garb......
  • 【风光不确定】基于多时间尺度滚动优化算法的主动配电网研究【IEEE33节点】(Matlab代码
    目录......
  • 8592 KMP算法
    首先,我们需要理解KMP算法的基本思想。KMP算法是一种改进的字符串匹配算法,它的主要思想是利用已经部分匹配的这个有效信息,使得后续的匹配中,尽量减少字符串的回溯,提高匹配效率。KMP算法的关键在于通过一个next数组,保存模式串中前后缀的最长的共有元素的长度。当模式串中的字符......
  • 信号量的秘密:Python中的Semaphore详解
    引言随着计算机硬件的发展,多核处理器已经成为了标准配置。这使得开发人员可以利用并发编程技术来提高应用程序的性能。然而,在并发环境下,资源共享和访问控制成了一个棘手的问题。Semaphore(信号量)就是一种常用的解决此类问题的技术。通过限制对共享资源的同时访问数量,Semapho......
  • Mybatis详细教程 (万字详解)
    Mybatis3.5.14来自于B站‘天气预报’,一名宝藏up,跟着他可以培养起独立解决编程问题的能力!!!01.简介1.1官网官方中文网:MyBatis中文网中文网参考手册1.2概念MyBatis是一款优秀的持久层框架,支持自定义SQL,存储过程,高级映射官方解释必要前置技术Mys......
  • AI大模型算法工程师就业宝典—— 高薪入职攻略与转行秘籍!
    从ChatGPT到新近的GPT-4,GPT模型的发展表明,AI正在向着“类⼈化”⽅向迅速发展。GPT-4具备深度阅读和识图能⼒,能够出⾊地通过专业考试并完成复杂指令,向⼈类引以为傲的“创造⼒”发起挑战。现有的就业结构即将发⽣重⼤变化,社会⽣产⼒的快速提升将催⽣新的⾏业和岗位机会。如......