迭代加深搜索(图的路径查找)

news2024/10/1 3:35:08

目录

概念

优点

缺点

如何剪枝(八数码)

剪枝策略:

深度优先搜索(DFS)和广度优先搜索(BFS)

深度优先搜索(DFS)

广度优先搜索(BFS)

比较

应用场景

经典案例(图的路径查找)

代码分析


概念

迭代加深搜索(Iterative Deepening DFS,IDDFS)是一种结合了深度优先搜索(DFS)和广度优先搜索(BFS)思想的搜索方法。它通过逐步增加搜索深度来寻找解决方案,每次限制搜索深度的DFS。如果在当前深度下找到了解决方案,那么就返回该解决方案;否则,增加搜索深度并重新开始搜索。

迭代加深搜索(Iterative Deepening Search, IDS)是一种结合了深度优先搜索(DFS)和广度优先搜索(BFS)特性的搜索算法。IDS的基本思想是从深度为1开始逐渐增加搜索的深度限制,直到找到目标或确定目标不存在为止。在每次迭代中,它使用深度优先搜索来遍历图,直到达到当前的深度限制。

优点

它可以在时间和空间上更有效地利用资源。它可以在搜索空间较小的情况下快速找到解决方案,而在搜索空间较大的情况下,则可以通过逐步增加搜索深度来避免过多的搜索。同时,由于它在层数上采用BFS思想来逐步扩大DFS的搜索深度,因此能够解决DFS可能陷入递归无法返回的问题,并避免BFS可能导致的队列空间爆炸问题。

缺点

它可能会导致重复搜索相同的状态,因为在每次增加搜索深度时,搜索树的一部分可能被重新搜索。此外,如果没有一个合适的方法来剪枝,迭代加深搜索也可能会很容易超时。

在实际应用中,迭代加深搜索通常用于解决搜索树非常深但答案可能在浅层节点的问题。通过结合DFS和BFS的思想,以及可能使用的剪枝技术,如IDA*估价函数,迭代加深搜索可以在一定程度上提高搜索效率。

如何剪枝(八数码)

八数码网上案例很多,这里我就不一一介绍了,想了解的同学,问下度娘即可

在八数码(Eight Puzzle)问题中,剪枝(pruning)是一种优化回溯搜索过程的策略,通过避免搜索那些明显不会得到解决方案的部分来减少搜索空间。对于八数码问题,剪枝通常基于评估函数(heuristic function)来实现,该函数能够估计从当前状态到达目标状态所需的最小步数。

剪枝策略:

  1. 使用评估函数:评估函数可以根据当前棋盘的排列情况来预测到达目标状态所需的最小步数。一个简单的评估函数可以计算每个数字与其在目标状态中的位置之间的距离之和。例如,如果数字1在初始棋盘上的位置为(x, y),在目标棋盘上的位置为(goalX, goalY),那么该数字对评估函数的贡献可以是abs(x - goalX) + abs(y - goalY)。将所有数字的贡献相加,得到当前棋盘的评估值。

  2. 在搜索过程中,可以设置一个最大步数限制。如果某个状态的评估值加上已经走过的步数大于或等于这个限制,则可以认为该状态不可能到达目标状态,因此可以剪枝。

  3. 对称剪枝
    由于八数码问题中的数字排列是对称的(例如,旋转或镜像),因此可以利用这些对称性来剪枝。如果在搜索过程中遇到了一个已经通过旋转或镜像处理过的状态,那么可以跳过该状态的进一步搜索。

  4. 逆序剪枝
    如果在搜索过程中遇到了一个之前已经搜索过的状态(即该状态已经在搜索树中出现过),那么可以剪枝,因为继续搜索该状态只会得到之前已经找到的结果。

  5. 深度优先搜索与广度优先搜索的选择
    深度优先搜索(DFS)和广度优先搜索(BFS)都可以用于解决八数码问题。由于我们希望找到的是最短解决方案,因此BFS通常更适合,因为它会首先探索较浅层的节点。然而,BFS需要存储所有已经访问过的状态,这可能会消耗大量的内存。DFS不需要存储所有状态,但可能需要更复杂的剪枝策略来确保找到最短路径。

深度优先搜索(DFS)和广度优先搜索(BFS)

深度优先搜索(DFS,Depth-First Search)和广度优先搜索(BFS,Breadth-First Search)是两种常用的图遍历算法,用于遍历或搜索树或图的节点。它们各自具有不同的特点和应用场景。

深度优先搜索(DFS)

深度优先搜索是一种用于遍历或搜索树或图的算法。这个算法会尽可能深地搜索树的分支。当节点v的所在边都已被探寻过,搜索将回溯到发现节点v的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。如果还存在未被发现的节点,则选择其中一个作为源节点并重复以上过程,整个进程反复进行直到所有节点都被访问为止。

DFS通常使用栈(stack)数据结构来实现,因为它需要后进先出(LIFO)的特性来保存搜索路径。

广度优先搜索(BFS)

广度优先搜索是一种用于遍历或搜索树或图的算法。这个算法从根节点(或任意节点)开始,探索最近邻的节点,然后再进一步探索下一个层次的节点,依此类推。BFS使用队列(queue)数据结构来保存待探索的节点,这使得它能够按照节点被发现的顺序(即层次遍历顺序)来访问它们。

BFS通常用于查找最短路径,例如在无权图中找到从源节点到目标节点的最短路径。

比较
  • 空间复杂度:DFS的空间复杂度通常较低,因为它只需要保存从源节点到当前节点的路径信息。然而,在最坏情况下,当图退化为链状时,DFS可能需要存储与图中节点数相同数量的信息。相比之下,BFS的空间复杂度可能更高,因为它需要存储所有已访问但尚未探索的节点。
  • 时间复杂度:在平均情况下,DFS和BFS的时间复杂度都是O(V + E),其中V是节点数,E是边数。然而,在某些特定情况下,如搜索树或图的结构特殊时,两者的性能可能会有所不同。
  • 应用:DFS常用于解决图论中的连通性问题、寻找桥或割点、拓扑排序等问题。BFS则常用于查找最短路径、解决迷宫问题、检测图中的环等问题。

应用场景

  1. 跨境电商物流路径优化:在跨境电商中,商品需要从仓库运送到客户手中,并可能经过多个转运中心。使用迭代加深搜索可以帮助找到最短或最经济的物流路径。通过将商品、供应商、客户和物流中心视为图中的节点,并利用迭代加深搜索来遍历这些节点及其关系,可以高效地找到最优路径。

  2. 人工智能游戏求解:在人工智能领域,迭代加深搜索常用于求解游戏的最优策略。例如,在棋类游戏中,玩家需要找到一系列的动作来赢得比赛。通过迭代加深搜索,AI可以逐步扩大搜索深度,从而找到能够赢得比赛的最优步骤序列。

  3. 图形设计和处理:在图形设计和处理中,迭代加深搜索可以用于寻找满足特定条件的图形结构。例如,在生成具有特定属性的图形或模式时,可以使用迭代加深搜索来探索可能的图形空间,并找到符合要求的解。

  4. 网络路由选择:在计算机网络中,路由器需要选择最佳的路径来传输数据包。迭代加深搜索可以帮助路由器在复杂的网络拓扑中找到最优的路由路径,确保数据包能够高效、准确地到达目的地。

  5. 知识图谱推理:在知识图谱中,节点代表实体,边代表实体之间的关系。迭代加深搜索可以用于在知识图谱中进行推理,找到满足特定条件的实体或关系路径。这对于智能问答、信息抽取等任务非常有用。

经典案例(图的路径查找)

package routine.suibi;

import java.util.*;

public class IterativeDeepeningSearch {

    // 图的节点类  
    static class Node {
        int id; // 节点编号  
        List<Node> neighbors; // 相邻节点列表  

        Node(int id) {
            this.id = id;
            this.neighbors = new ArrayList<>();
        }

        // 添加相邻节点  
        void addNeighbor(Node node) {
            neighbors.add(node);
        }

        @Override
        public String toString() {
            return "Node{" + "id=" + id + '}';
        }
    }

    // 路径类  
    static class Path {
        List<Node> nodes; // 路径上的节点列表  
        int depth; // 路径深度  

        Path(Node startNode) {
            this.nodes = new ArrayList<>();
            this.nodes.add(startNode);
            this.depth = 1;
        }

        // 添加节点到路径中  
        void addNode(Node node) {
            nodes.add(node);
            depth++;
        }

        // 获取路径上的最后一个节点  
        Node getLastNode() {
            return nodes.get(nodes.size() - 1);
        }

        @Override
        public String toString() {
            return "Path{" + "nodes=" + nodes + ", depth=" + depth + '}';
        }
    }

    // 迭代加深搜索算法  
    public static Path iterativeDeepeningSearch(Node start, Node goal) {
        int maxDepth = 0; // 初始化最大深度为0  
        while (true) {
            Path result = dfs(start, goal, maxDepth);
            if (result != null) {
                return result; // 找到路径,返回结果  
            }
            maxDepth++; // 增加最大深度限制,继续搜索  
            if (maxDepth > getMaxDepth(start, goal)) {
                break; // 超过最大可能深度,退出循环  
            }
        }
        return null; // 没有找到路径  
    }

    // 深度优先搜索辅助方法  
    private static Path dfs(Node current, Node goal, int maxDepth) {
        if (current.equals(goal)) {
            return new Path(current); // 到达目标节点,返回路径  
        }
        if (maxDepth <= 0) {
            return null; // 达到最大深度限制,返回null  
        }

        for (Node neighbor : current.neighbors) {
            Path result = dfs(neighbor, goal, maxDepth - 1); // 递归搜索相邻节点  
            if (result != null) {
                result.addNode(current); // 将当前节点添加到路径中  
                return result; // 返回找到的路径  
            }
        }
        return null; // 没有找到路径  
    }

    // 获取从起点到终点的最大可能深度(可选,用于提前退出循环)  
    private static int getMaxDepth(Node start, Node goal) {
        Queue<Node> queue = new LinkedList<>();
        Set<Node> visited = new HashSet<>();
        queue.add(start);
        visited.add(start);
        int depth = 0;

        while (!queue.isEmpty()) {
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                Node current = queue.poll();
                if (current.equals(goal)) {
                    return depth; // 找到目标节点,返回当前深度  
                }
                for (Node neighbor : current.neighbors) {
                    if (!visited.contains(neighbor)) {
                        queue.add(neighbor);
                        visited.add(neighbor);
                    }
                }
            }
            depth++;
        }
        return -1; // 没有找到目标节点,返回-1(理论上这种情况不会发生,因为图是连通的)  
    }

    public static void main(String[] args) {
        // 创建图的节点和边  
        Node A = new Node(1);
        Node B = new Node(2);
        Node C = new Node(3);
        Node D = new Node(4);

        // 添加边
        A.addNeighbor(B);
        A.addNeighbor(C);
        B.addNeighbor(C);
        B.addNeighbor(D);
        C.addNeighbor(D);

        // 执行迭代加深搜索
        Path path = iterativeDeepeningSearch(A, D);

        // 输出结果
        if (path != null) {
            System.out.println("找到路径: " + path);
        } else {
            System.out.println("没有找到路径");
        }
    }
}

代码分析

  1. 节点和路径类定义
    • Node 类表示图中的一个节点,包含一个编号 id 和一个邻居节点列表 neighbors
    • Path 类表示从起点到某个节点的路径,包含一个节点列表 nodes 和路径的深度 depth
  2. 迭代加深搜索方法 iterativeDeepeningSearch
    • 该方法接受起点 start 和目标节点 goal 作为参数,返回从起点到目标节点的路径。
    • 使用一个循环来逐渐增加最大深度限制 maxDepth,并在每次迭代中调用深度优先搜索方法 dfs
    • 如果 dfs 方法返回非空路径,则返回该路径。
    • 如果达到一个可能的最大深度(通过 getMaxDepth 方法获得)而仍未找到路径,则退出循环。
  3. 深度优先搜索辅助方法 dfs
    • 该方法递归地遍历图,从当前节点 current 开始搜索目标节点 goal
    • 如果当前节点就是目标节点,则创建一个新的 Path 对象并返回。
    • 如果当前深度 maxDepth 为0或小于0,则返回 null,表示已达到深度限制。
    • 否则,遍历当前节点的所有邻居节点,并对每个邻居节点递归调用 dfs 方法。
    • 如果在邻居节点中找到路径,将该路径与当前节点合并(添加到路径的开头),并返回合并后的路径。
  4. 获取最大深度的方法 getMaxDepth(可选):
    • 该方法使用广度优先搜索(BFS)来计算从起点到终点的最短路径长度(即最大深度)。
    • 这可以帮助我们在迭代加深搜索中设置合理的深度限制,避免不必要的搜索。
    • 在实践中,如果图很大或者结构复杂,这个计算可能会很耗时,因此可以省略这一步,而只是不断增加深度限制直到找到路径或确定路径不存在。
  5. 主方法 main
    • 在 main 方法中,我们创建了一个简单的图,并添加了边来连接节点。
    • 然后,我们调用 iterativeDeepeningSearch 方法来查找从节点A到节点D的路径。
    • 最后,我们打印出找到的路径(如果存在)或未找到路径的消息

它能够在空间消耗较小的情况下找到较短的路径,并且避免了深度优先搜索可能陷入无限递归的问题(当存在环路时)。同时,通过逐渐增加深度限制,它能够在一定程度上模拟广度优先搜索的行为,最终找到从起点到终点的路径(如果存在的话)。

创作不易,感谢各位大佬的支持鼓励。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1614056.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Facebook账号运营要用什么IP?

众所周知&#xff0c;Facebook封号大多数情况都是因为IP的原因。Facebook对于用户账号有严格的IP要求和限制&#xff0c;以维护平台的稳定性和安全性。在这种背景下&#xff0c;海外IP代理成为了一种有效的解决方案&#xff0c;帮助用户避免检测&#xff0c;更加快捷安全地进行…

基于51单片机的电子秤LCD1602液晶显示( proteus仿真+程序+设计报告+讲解视频)

基于51单片机电子秤LCD显示 1. 主要功能&#xff1a;2. 讲解视频&#xff1a;3. 仿真设计4. 程序代码5. 设计报告6. 设计资料内容清单&&下载链接 基于51单片机电子秤LCD显示( proteus仿真程序设计报告讲解视频&#xff09; 仿真图proteus8.9及以上 程序编译器&#xf…

OceanBase开发者大会2023届视频及PPT汇总

数据库技术趋势 我眼中的数据库技术 阳振坤OceanBase 首席科学家 观看视频 下载 PDF 未来&#xff0c;中国需要什么样的数据库&#xff1f; 周傲英华东师范大学副校长&#xff0c;CCF 会士 观看视频 下载 PDF 云原生技术趋势解读 Keith ChanCNCF 云原生计算基金会中国区总监 …

Linux嵌入式驱动开发-阻塞IO与非阻塞IO

文章目录 阻塞与非阻塞访问简介阻塞访问的实现等待队列等待队列头等待队列项从等待队列头添加/移除等待队列项等待唤醒等待事件API 非阻塞访问的实现轮询poll 函数原型可以返回的资源状态 阻塞与非阻塞访问简介 **IO&#xff1a;**Input/Output&#xff0c;也就是输入/输出&am…

【无标题】vscode 配置c++ c编译环境

不用图形化也可以直接把launcher.json c_c_properties.json task.json复制到项目里 首先打开 vscode创建项目 ctrlshiftp 打开c/c edit configuration UI 配置生成c_cpp_properties.json文件 这里选择gcc为 c运行环境 只需要改配置名称跟编译器路径两处其他默认 选g为c环境 可…

Docker容器化部署(企业版)

大家好&#xff0c;webfunny前端监控埋点系统&#xff0c;已经正式发布了webfunny的官方镜像&#xff1a; Webfunny镜像目录&#xff1a;https://hub.docker.com/r/webfunny/webfunny_monitor_cluster/tags 部署前提是你的服务器已经安装了Docker环境&#xff0c;没有安装doc…

pygame 烟花效果

# 初始化 pygame.init() screen_width 800 screen_height 600 screen pygame.display.set_mode((screen_width, screen_height)) pygame.display.set_caption(烟花效果) # 焰火发射 particles [] # 焰火粒子 def firework(x, y): num_particles 100 # 每次发射的…

Springboot+Vue项目-基于Java+MySQL的影城管理系统(附源码+演示视频+LW)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;Java毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计 &…

【Python-面向对象】

Python-面向对象 ■ 事物和类■ 成员方法定义和使用■ self■ 构造方法__init__&#xff08;&#xff09;■ 魔术方法■ __str__字符串方法■ __lt__小于、大于符号比较■ __le__小于等于、大于等于符号比较■ __eq__符号比较■ 综合演示 ■ 封装■ 私有成员和方法 ■ 继承■ 单…

STM32之HAL开发——ILI9341液晶控制器

ILI9341液晶控制器简介 本液晶屏内部包含有一个液晶控制芯片ILI9341&#xff0c;它的内部结构非常复杂&#xff0c;如下图。该芯片最主核心部分是位于中间的GRAM(Graphics RAM)&#xff0c;它就是显存。GRAM中每个存储单元都对应着液晶面板的一个像素点。它 右侧的各种模块共同…

Ubuntu20.04 ISAAC SIM仿真下载使用流程

机器&#xff1a;华硕天选X2024 显卡&#xff1a;4060Ti ubuntu20.04 安装显卡驱动版本&#xff1a;525.85.05 参考&#xff1a; What Is Isaac Sim? — Omniverse IsaacSim latest documentationIsaac sim Cache 2023.2.3 did not work_isaac cache stopped-CSDN博客 Is…

聚观早报 | TCL召开电视新品发布会;OceanBase 4.3发布

聚观早报每日整理最值得关注的行业重点事件&#xff0c;帮助大家及时了解最新行业动态&#xff0c;每日读报&#xff0c;就读聚观365资讯简报。 整理丨Cutie 4月22日消息 TCL召开电视新品发布会 OceanBase 4.3发布 科大讯飞推出耳背式助听器 F1联想中国大奖赛开赛 蔚来展…

力扣——并查集算法系列

【LeetCode 684. 冗余连接】 思路&#xff1a; 首先因为这是一个无向图&#xff0c;所以不需要考虑谁是树根。 那么我们一条条边加入到图里去&#xff0c;直到出现了环为止&#xff0c;那么这条边就是冲突的边&#xff0c;需要删除掉。 那么怎么判断是否出现了环呢&#xff…

36. 【Android教程】侧滑菜单:DrawerLayout

侧滑菜单是用来在页面上增加一个抽屉式菜单栏的控件&#xff0c;它一般位于左侧&#xff0c;用户可以通过侧滑拉出或者关闭。通常你可以放置一些菜单项或者上下文相关的设置在里面&#xff0c;帮助你节省屏幕空间同时可以很方便的随时打开。侧滑菜单其实就是下面这货&#xff1…

python 对图片进行操作

Pillow是一个强大的图像处理库&#xff0c;它提供了许多用于打开、操作和保存图像的功能。 Image模块&#xff1a; Image模块提供了用于打开、创建、编辑和保存图像的基本功能。可以使用Image.open()函数来打开图像文件&#xff0c;或者使用Image.new()函数来创建新的图像,还可…

【Java框架】Spring框架(六)——Spring中的Bean的作用域

目录 Bean的作用域1.singleton(默认)代码示例 2.prototype代码示例 3.request代码示例 4.session代码示例 5.application代码示例 websocket Bean的作用域 Spring支持6个作用域&#xff1a;singleton、prototype、request、session、application、websocket 1.singleton(默认…

山与路远程控制 一个基于electron和golang实现的远控软件

山与路远程控制 &#x1f3a5;项目演示地址 还在制作… ♻️项目基本介绍 山与路远程控制是基于electron(vue3)和golang实现的远程控制软件(项目界面主要模仿向日葵远程软件,如有侵权请告知),代码可能有点臃肿毕竟只花了一周左右写的无聊项目,如果对其感兴趣的大佬可以fork自…

【信号处理】心电信号传统R波检测定位典型方法实现(matlab)

关于 心电信号中QRS波检测是一个非常重要的步骤&#xff0c;可以用于实现重要波群的基本定位&#xff0c;在定位基础上&#xff0c;可以进一步分析心电信号的特征变化&#xff0c;从而为医疗诊断提供必要的参考。 工具 MATLAB ECG心电信号 方法实现 ECG心电信号加载 ecg …

Java中的数组(上)

1.怎样定义Java中的数组 package day40; ​ public class day25 {public static void main(String[] args) {int[] array1{1,2,3,4,5};int[] array2new int[10];for (int i 0; i < array1.length; i) {System.out.print(array1[i]" ");}System.out.println();fo…

学习笔记:Vue2中级篇

Vue2 学习笔记&#xff1a;Vue2基础篇_ljtxy.love的博客-CSDN博客学习笔记&#xff1a;Vue2中级篇_ljtxy.love的博客-CSDN博客学习笔记&#xff1a;Vue2高级篇_ljtxy.love的博客-CSDN博客 Vue3 学习笔记&#xff1a;Vue3_ljtxy.love的博客&#xff09;-CSDN博客 文章目录 5.…