人工智能 - A*算法解决迷宫问题 附源码和可视化显示

这篇具有很好参考价值的文章主要介绍了人工智能 - A*算法解决迷宫问题 附源码和可视化显示。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

写在最前,先附上可视化后的效果:

利用搜索算法完成机器人走迷宫问题,算法,人工智能,vue

一.问题描述

迷宫问题可以表述为:一个二维的网格,0 表示点可走,1 表示点 不可以走,点用(x,y)表示,寻找从某一个给定的起始单元格出发, 经由行相邻或列相邻的单元格(可以通过的),最终可以到达目标单元 格的、所走过的单元格序列。在任一个单元格中,都只能看到与它邻 近的 4 个单元格(如果位于底边,则只有 3 个;位于 4 个角上,则只 有 2 个是否能通过)。

该问题可用A*启发式搜索算法的思想求解。

利用搜索算法完成机器人走迷宫问题,算法,人工智能,vue

 本次评估函数中的

        g(n):从起点当前点所走的步数

        h(n):当前点终点之间的曼哈顿距离

利用搜索算法完成机器人走迷宫问题,算法,人工智能,vue


二.算法实现【理论部分】 

1.对问题的抽象

①.操作算子的选取

在不考虑附加条件的情况下,迷宫中的每一个数码块可选择的操作算子有

- 上移 UP  - 下移 DOWN - 左移 LEFT  - 右移 RIGHT

利用搜索算法完成机器人走迷宫问题,算法,人工智能,vue

②.执行操作算子的抽象过程

可将迷宫抽象为一个二维数组(以下图3*3的迷宫为例),这样每次数码块在迷宫中的移动将抽象为二维数组下标的变化,如下图的例子所示:

a.【①号】 执行【RIGHT操作】 移动到 【③号】的位置 

 - 下标变化:(0,0) => (0,1)

b. 【①号】 执行【DOWN操作】 移动到 【②号】的位置

 - 下标变化:(0,0) => (1,0)

利用搜索算法完成机器人走迷宫问题,算法,人工智能,vue

至此,数码在迷宫中的移动问题转为为数码的二维数组下标转换的问题。

不难得出   UP操作        = 行坐标  - 1        列坐标不变

                 DOWN操作  = 行坐标 + 1        列坐标不变

                 LEFT操作    = 行坐标不变         列坐标 - 1

                 RIGHT操作  = 行坐标不变        列坐标 + 1

③.操作算子的限制条件

     在实际执行过程中,每个节点并不是任何情况下都能执行四个操作算子的,操作算子的选取应该具有如下几个限制条件:

        A.迷宫四周边界的限制条件

        这个非常好理解,不过多解释了,直接看图:

利用搜索算法完成机器人走迷宫问题,算法,人工智能,vue

为避免此类现象,可在选取操作算子时加上对于边界的限制条件。当数码块的二维坐标满足边界条件时直接禁用相应的操作算子。 

        B.防止循环

        前一次执行了UP操作,下一次操作算子选取应该禁用DOWN操作

        前一次执行了LEFT操作,下一次操作算子选取应该禁用RIGHT操作

        反之亦然,目的是为了避免出现程序在两个互相可达的数码块之间无限往返

      为避免此类现象,在设计数据结构时可为每个节点保存上一次执行的操作(lastOperation)

         C.迷宫中原有的不可达的点

        实际情况下,用户可限制迷宫中的某些块不可走(参考"下一小节"实际执行过程"中 4*4迷宫图")在程序中体现为灰色块可走,白色块不可走。

为实现该种限制条件,可为每个二维数组的元素设置对应的值:

0 表示 该数码块 可走      =》灰色

1 表示 该数码块 不可走  =》白色

为每个块设置不同的值后,在选取操作算子时以此作为判断条件,以此判断相应的操作算子是否可被选取。

举例说明: ①号在不考虑限制条件的情况下可选取DOWNRIGHT操作分别到达,但由于②号块的位置人为设置的不可走,因此不能选取RIGHT作为的操作算子

利用搜索算法完成机器人走迷宫问题,算法,人工智能,vue

 ④.无解的情况

对于无解的情况需要进行单独处理,以便算法出现无法预计的错误。

起点终点在迷宫初始化时本身不可达时,即其可选择的操作算子为0时,则不用执行寻路算法,直接返回该情况无解。用图例给出几种操作算子选取为0的常见情况。

只要起点或终点其中一个点满足不可达操作算子为0的条件,本次寻路算法便无需执行。因此在每次进行A*搜索路径前应该先判断起点和终点是否满足【操作算子为0】的条件。

 文章来源地址https://www.toymoban.com/news/detail-781834.html

利用搜索算法完成机器人走迷宫问题,算法,人工智能,vue

 2.实际执行过程

以下图4*4的迷宫为例:

  - 起点为(0,0) 终点为(3,3)  【二维数组下标从0开始】

  - 灰色块可走的数码块

  - 白色块不可走的数码块

利用搜索算法完成机器人走迷宫问题,算法,人工智能,vue

①.搜索树的建立

        为简化搜索树,对灰色块进行如下的编号 

利用搜索算法完成机器人走迷宫问题,算法,人工智能,vue

根据上图绘制如下的搜索树:

利用搜索算法完成机器人走迷宫问题,算法,人工智能,vue

 由上图的搜索树可知:

        树中的每一个节点应该包含如下信息:

        - 该点在树中的深度 deep

        - 该点的估价函数值fn

        - 该点的父节点father (用于溯源)

 ②.open表和closed表

open表中存放的是:按估价函数值fn排序的节点,

closed表中存放的是:每次从open表中取出的第一个节点

直到closed表中取到终止节点时迷宫寻路结束。

利用搜索算法完成机器人走迷宫问题,算法,人工智能,vue

③.执行结果分析:

根据①中的搜索树和②中的open表和closed表分析可知:

根据例子可知【起点①-终点⑩】之间应该有两条路可达,分别为:

线路一: ①-②-③-⑥-⑧-⑨-⑩

线路二: ①-②-③-④-⑤-⑦-⑩

当起点和终点之间有多条估价函数值相同的的路径时,先找到谁则先返回谁。

假设本次执行结果返回的线路为 ①-②-③-⑥-⑧-⑨-⑩,具体可见下图:

利用搜索算法完成机器人走迷宫问题,算法,人工智能,vue

 3.数据结构设计

        根据前文的各自分析,程序中最重要的数据结构是closed表中的每一个节点的结构。我对每一个节点的数据结构进行了如下的设计:

      mazePoint: {
        rowIndex: 0,        // 二维数组行索引
        colIndex: 0,       // 二维数组列索引
        lastOperation: "", // 该点由何种操作算子执行得到 
        deep: 0,          // 该点在搜索树中的深度
        fn: 0,            // 该点的估价函数值
        father:{}         // 该点的父亲节点【用于溯源路径】
      },

三.算法实现【代码部分】

由于博主在编写时硬性要求需要有简单的界面并且用户可自定义迷宫的长宽和形状,我不想每次都输入很长的二维数组,于是编写时直接选用了JavaScript配合Vue,这样可用交互点击的方式代替繁琐的输入,DEMO已部署在公网了,读者可自行访问网站使用可视化的方式看到每次迷宫问题的解。

使用方法参考网站左侧的【使用步骤】或下方GIF图

网址在这:A*算法 可视化网站https://sls-website-ap-nanjing-evpmzcc-1313270013.cos-website.ap-nanjing.myqcloud.com/

放一个效果图:

利用搜索算法完成机器人走迷宫问题,算法,人工智能,vue

 

1.流程图

利用搜索算法完成机器人走迷宫问题,算法,人工智能,vue

 

2.源码

整个Vue项目小文件很多,这里只贴出主要的JS部分逻辑代码:

所有函数的功能在源码中基本都有注释了,有不懂的地方欢迎交流。

贴出DEMO的github仓库地址,感兴趣的读者可自行去仓库查看源码

A164759920/A--maze (github.com)https://github.com/A164759920/A--maze

代码小结:

        - Vue2更新二维数组不能通过简单的赋值,需要使用this.$set

        - 涉及引用数据类型的拷贝时,需要注意是深拷贝还是浅拷贝

                - 若为深拷贝,推荐用Object.assign({},待拷贝对象)

        - input框双向绑定的值默认读取类型为String(编写过程中貌似是这样)

                - 若需要Number类型,使用Number()进行类型转换

        - 可用shiftpop简化对数组的操作

        - 可用动态class实现背景色切换

<script>
export default {
  name: 'HelloWorld',
  data: function () {
    return {
      rowNum: 1,  // 接收用户输入的行数
      colNum: 1,    // 接收用户输入的列数
      startInput: "0,0", // 接收用户输入的起点坐标
      endInput: "0,0",    // 接收用户输入的终点坐标
      startPoint: {    // 起点
        rowIndex: 0,
        colIndex: 0,
        lastOperation: "",
        deep: 0,
        fn: 0,
      },
      endPoint: {    // 终点
        rowIndex: 0,
        colIndex: 0,
        lastOperation: "",
        deep: 0,
        fn: 0,
      },
      mazeArray: [    // 迷宫二维数组
      ]
    }
  },
  methods: {
    /**
     * 设置迷宫块的类名
     * @param {String} rowIndex 行索引
     * @param {String} colIndex 列索引
     */
    setColorClass: function (rowIndex, colIndex) {
      let flag = ""
      switch (this.mazeArray[rowIndex][colIndex]) {
        case 0:
          flag = `selected`
          break;
        case 1:
          break;
        case 2:
          flag = `steped`
          break;
        case 3:
          flag = `start-end`
          break;
      }
      return flag
    },
    /**
     * 迷宫块点击事件
     * @param {String} rowIndex 
     * @param {Srting} colIndex 
     */
    setArray: function (rowIndex, colIndex) {
      // this.mazeArray[rowIndex][colIndex] = 1
      this.$set(this.mazeArray[rowIndex], colIndex, 0)
    },
    /**
     * 设置计算路径前的所有参数 
     */
    saveParams: function () {
      const tempStart = this.startInput.split(",")
      // 先pop列后pop行
      this.startPoint.colIndex = Number(tempStart.pop())
      this.startPoint.rowIndex = Number(tempStart.pop())
      const tempEnd = this.endInput.split(",")
      // 先pop列后pop行
      this.endPoint.colIndex = Number(tempEnd.pop())
      this.endPoint.rowIndex = Number(tempEnd.pop())
      this.initParams()
      this.$set(this.mazeArray[this.startPoint.rowIndex], this.startPoint.colIndex, 0)
      this.$set(this.mazeArray[this.endPoint.rowIndex], this.endPoint.colIndex, 0)
    },
    /**
     * 计算路径并设置迷宫块颜色
     */
    getPath: function () {
      // 先判断起点和终点旁边是否有一步可达的通路,没有则一定不可达
      const isEndConnect = this.selectOperation("", this.endPoint.rowIndex, this.endPoint.colIndex)
      const isStartConnect = this.selectOperation("", this.startPoint.rowIndex, this.startPoint.colIndex)
      if (isEndConnect.length > 0 && isStartConnect.length > 0) {
        // 计算路径
        const closedList = this.computedRoute()
        // 回溯路径
        // 设置起点/终点样式
        this.$set(this.mazeArray[this.startPoint.rowIndex], this.startPoint.colIndex, 3)
        this.$set(this.mazeArray[this.endPoint.rowIndex], this.endPoint.colIndex, 3)
        console.log("最终closed表", closedList)
        // TODO:溯源需要修改
        let Father = closedList.pop().father
        while (Father.deep >= 1) {
          this.$set(this.mazeArray[Father.rowIndex], Father.colIndex, 2)
          Father = Father.father
        }
      } else {
        alert("起点-终点之间不可达")
      }

    },
    /**
     * 重写sort排序
     * @param {String} props 排序属性名
     */
    sortBy: function (props) {
      return function (a, b) {
        return a[props] - b[props]
      }
    },
    /**
     * 初始化迷宫二维数组
     */
    initParams: function () {
      //创建二维数组
      const arr = new Array(Number(this.rowNum))
      for (var i = 0; i < arr.length; i++) {
        arr[i] = new Array(Number(this.colNum)).fill(1)
      }
      this.mazeArray = arr
    },
    /**
     * 获取当前迷宫块可选择的操作算子
     * @param {String} lastOperation 上一次的操作算子
     * @param {String} rowIndex 行索引
     * @param {String} colIndex 列索引
     */
    selectOperation: function (lastOperation, rowIndex, colIndex) {
      const selectd = []
      // up操作
      if (lastOperation !== "down" && rowIndex !== 0) {
        if (this.mazeArray[rowIndex - 1][colIndex] === 0) {
          selectd.push("up")
        }
      }
      // down操作
      if (lastOperation !== "up" && rowIndex !== this.rowNum - 1) {
        if (this.mazeArray[rowIndex + 1][colIndex] === 0) {
          selectd.push("down")
        }
      }
      // left操作
      if (lastOperation !== "right" && colIndex !== 0) {
        if (this.mazeArray[rowIndex][colIndex - 1] === 0) {
          selectd.push("left")
        }
      }
      // right操作
      if (lastOperation !== "left" && colIndex !== this.colNum - 1) {
        if (this.mazeArray[rowIndex][colIndex + 1] === 0) {
          selectd.push("right")
        }
      }
      return selectd
    },
    /**
     * 获取执行操作算子后的迷宫块坐标
     * @param {String} operation 操作算子
     * @param {String} currentPoint 执行操作的迷宫块
     */
    getOperatedPoint(operation, currentPoint) {
      switch (operation) {
        case "up":
          currentPoint.rowIndex -= 1
          break;
        case "down":
          currentPoint.rowIndex += 1
          break;
        case "left":
          currentPoint.colIndex -= 1
          break;
        case "right":
          currentPoint.colIndex += 1
          break;
      }
      return currentPoint
    },
    /**
     * 计算曼哈顿距离
     * @param {String} rowIndex 
     * @param {String} colIndex 
     */
    computed_MHT_Distance(rowIndex, colIndex) {
      const distance = Math.abs(rowIndex - this.endPoint.rowIndex) + Math.abs(colIndex - this.endPoint.colIndex)
      return distance
    },
    /**
     * A*思想计算路线函数
     */
    computedRoute: function () {
      const that = this
      let openList = []
      openList.push(that.startPoint)
      const closedList = []
      let count = 1
      while (true) {
        if (openList[0].rowIndex === this.endPoint.rowIndex && openList[0].colIndex === this.endPoint.colIndex) {
          // 更新closed表
          closedList.push(openList[0])
          closedList.shift()
          // 更新open表
          openList.shift()
          // 退出循环
          // console.log("最终closed表", closedList)
          return closedList
        } else {
          // 操作算子集
          const currentOperation = this.selectOperation(openList[0].lastOperation, openList[0].rowIndex, openList[0].colIndex)
          // 遍历操作算子集,构建每一个Point对象
          currentOperation.forEach(operation => {
            const currentPoint = Object.assign({}, openList[0]) // 深拷贝
            const fatherPoint = Object.assign({}, openList[0]) // 深拷贝
            const nextPoint = that.getOperatedPoint(operation, currentPoint)
            const openlistObj = {
              rowIndex: nextPoint.rowIndex,
              colIndex: nextPoint.colIndex,
              lastOperation: operation,
              deep: currentPoint.deep + 1,
              fn: currentPoint.deep + 1 + that.computed_MHT_Distance(nextPoint.rowIndex, nextPoint.colIndex),
              father: fatherPoint
            }
            console.log(`第${count}次open表.元素`, openlistObj)
            openList.push(openlistObj)
          })
          // 更新closed表
          closedList.push(openList[0])
          // 更新open表
          openList.shift()
          openList.sort(that.sortBy("fn"))
          // 打印open表和closed表
          count = count + 1
        }
      }
    }
  },
}
</script>

写在最后:

        博主也是代码小白,若读者在阅读过程中发现问题欢迎留言交流。

                                                                                                        -  写于2022年最后一天

 

到了这里,关于人工智能 - A*算法解决迷宫问题 附源码和可视化显示的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • 人工智能-A*算法-八数码问题

    人工智能-A*算法-八数码问题

    一,A*算法设计思想 A* 算法(A-star )是一种寻路算法 ,主要用于游戏、机器人等领域。 它的设计思想是将最短路径搜索问题转化为一个优化问题,通过计算每个节点的评分(f(n) = g(n) + h(n))来寻找最优路径。 以下是 A*算法的设计思想: 1.  引入启发式函数 (h(n)): A*算法

    2024年04月15日
    浏览(40)
  • 人工智能导论——遗传算法求解TSP问题实验

    人工智能导论——遗传算法求解TSP问题实验

    一、实验目的: 熟悉和掌握遗传算法的原理、流程和编码策略,并利用遗传算法求解组合优化问题,理解求解TSP问题的流程并测试主要参数对结果的影响。 二、实验原理: 旅行商问题,即TSP问题(Traveling Salesman Problem)是数学领域中著名问题之一。假设有一个旅行商人要拜

    2023年04月13日
    浏览(10)
  • 人工智能与人类智能的解决问题能力在人工智能应用领域的实践

    人工智能(Artificial Intelligence, AI)是一门研究如何让计算机模拟人类智能行为的科学。人类智能包括学习、理解语言、认知、决策等多种能力。人工智能的目标是让计算机具备类似于人类智能的能力,以解决复杂的问题。 在过去的几十年里,人工智能技术已经取得了显著的进展

    2024年02月20日
    浏览(14)
  • 人工智能原理实验4(1)——遗传算法、蚁群算法求解TSP问题

    人工智能原理实验4(1)——遗传算法、蚁群算法求解TSP问题

    TSP问题是组合数学中一个古老而又困难的问题,也是一个典型的组合优化问题,现已归入NP完备问题类。NP问题用穷举法不能在有效时间内求解,所以只能使用启发式搜索。遗传算法是求解此类问题比较实用、有效的方法之一。下面给出30个城市的位置信息: 应用遗传算法和蚁

    2024年01月24日
    浏览(9)
  • 人工智能与创新:如何解决实际问题

    人工智能(Artificial Intelligence, AI)是一种计算机科学的分支,旨在让计算机具有人类般的智能。人工智能的目标是让计算机能够理解自然语言、识别图像、解决问题、学习和自主决策等。人工智能技术的应用范围广泛,包括机器学习、深度学习、自然语言处理、计算机视觉、语

    2024年02月19日
    浏览(9)
  • 人工智能技能的融合:实现高效问题解决

    随着人工智能技术的不断发展,人工智能技能的融合成为了实现高效问题解决的关键。人工智能技能的融合是指在人工智能系统中,将多种人工智能技术相互结合,共同完成某个任务的过程。这种融合可以让人工智能系统具备更强大的问题解决能力,更高效地处理复杂问题。

    2024年02月22日
    浏览(14)
  • 人工智能的未来:如何提高解决未知问题的能力

    人工智能(Artificial Intelligence, AI)是一种使计算机能够像人类一样智能地思考、学习和理解自然语言的技术。它的目标是创造出能够自主地解决问题、学习新知识和理解环境的智能系统。在过去的几十年里,人工智能技术已经取得了显著的进展,尤其是在机器学习、深度学习和

    2024年02月22日
    浏览(43)
  • 人工智能在未知问题解决领域的挑战与机遇

    人工智能(Artificial Intelligence, AI)是一门研究如何让计算机模拟人类智能的科学。在过去的几十年里,人工智能已经取得了很大的进展,例如自然语言处理、计算机视觉、机器学习等领域。然而,在未知问题解决领域,人工智能仍然面临着很大的挑战。 未知问题(Unkown Problems)是

    2024年02月21日
    浏览(11)
  • 认知科学与人工智能:解决复杂问题的关键

    人工智能(Artificial Intelligence, AI)是一门研究如何让计算机模拟人类智能的学科。人类智能可以分为两类:一类是通过学习和经验而获得的,称为“泛化”(generalization);另一类是通过直接学习而获得的,称为“特定”(specific)。人工智能的目标是让计算机具有这两种智能

    2024年01月17日
    浏览(8)
  • 人工智能与物理学:如何解决复杂的物理问题

    物理学是一门研究自然界各种物质和现象的科学。在过去的几十年里,物理学家们已经解决了许多复杂的物理问题,如黑洞、宇宙膨胀和量子力学等。然而,随着科学和技术的发展,物理学问题变得越来越复杂,需要更高效、更有效的方法来解决它们。 人工智能(AI)是一种通

    2024年02月20日
    浏览(16)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包