2.0简介:智能系统中的搜索
强调搜索如何成为日常生活的一个基本方面,并将这一概念扩展到人工智能。 如果数据按照逻辑顺 序组织的话,那么搜索起来就会比较方便。 解释了状态空间图(state-space graph),这是一种有 助于形式化搜索过程的数学结构。 解释了生成-测试 (generate-and-test)搜索范式。生成器模块系统地提出了问题的可能解,而测试器模块则验证这些解的正确性。
贪心算法(greedy algorithm)和回溯法(backtracking)。两种搜索方法的具体选择标准互不相同。 解释了盲目搜索(Blind Search,也称无信息搜索)算法。两种典型的盲目搜索算法是广度优先搜 索(Breath First Search,BFS)和深度优先搜索(Depth First Search,DFS)算法。
2.1状态空间图 状态空间图(state-space graph)是对问题的一种表示方法。通过状态空间图,人们可以探 索和分析通往解的可能的可选路径。本章主要关注盲目搜索。 假币问题是计算机科学中的一个经典问题。也是计算机科学中解决问题的隐喻。 它说明了如何利用有限的信息和资源系统地解决问题。 解决方案涉及通过逻辑推理和策略权衡进行消除的过程。 每次称重都提供有助于缩小可能性的信息,要么确认各组硬币的相等性,要么表明哪一组包含假币。它展示了设计有效策略来解决约束条件下和信息不完整的问题的重要性。 6 个硬币(C1、C2、...、C6)的假币问题的状态空间图。 等号代表平衡(相等),不等号代表不平衡(不相等)
- 初始状态 [C1 C2 C3 C4 C5 C6]:这表示所有六枚硬币都是假币的候选者。
- 第一次称重[C1 C2; C3 C4]:将两套硬币相互称重:C1 和 C2,C3 和 C4。
- 如果它们平衡(相等),则意味着假币是 C5 或 C6。
- 如果它们不平衡(不相等),则意味着假币属于 C1、C2、C3 或 C4。
- 如果第一次称重相同,则进行第二次称重 [C5 C6; C1 C5]:现在将C1 与 C5 进行权衡。
- 如果它们平衡,则 C6 是假币,因为从第一次称重就知道 C1 和 C5 是真币。
- 如果它们不平衡,C5 就是假币,因为真币 (C1) 不会使天平倾斜。
- 如果第一次称重不相等则进行第二次称重 [C1 C2; C5 C6]:将 C1 和 C2 与 C5 和 C6 进行称重,已知它们是真币。
- 如果平衡,则说明 C1 和 C2 都是真币,因此假币一定是 C3 或 C4。
- 如果不平衡,则假币为 C1 或 C2,因为是与真币进行比较。
- 如果第二次相等则第三次称重 [C3 C4; C1 C3]:在这里,将 C3 和 C4 与 C1(已知是正品)和 C3 进行权衡。
- 如果它们平衡,则 C4 是假币,因为 C3 是与真币一起称重的。
- 如果它们不平衡,出于同样的原因,C3 就是假币。
- 如果第二次不等于则第三次称重 [C1 C2; C1 C4]:将 C1 和 C2 与 C1(已知是正品)和 C4 进行权衡。
- 如果它们平衡,则 C2 是假币,因为 C1 是真币。
- 如果它们不平衡,C1 就是假币,因为它是与已知的真币进行比较。 状态空间图的每一步都在逻辑上缩小可能性,直到识别出假币。 关键是每次称重都提供了消除可能性的信息,而且过程是系统的,确保在第三次称重结束时,可以确定地识别出假币。 2.2生成-测试范式 2.2.1回溯法 问题求解的一种直接方式就是先给出可能的解,再检查每个可能的解,看是否有候选解能够构成问题的解。这种方式被称为生成-测试范式(generate-and-test paradigm)。 生成-测试范式是最基本的方法,可以通过使生成器更具信息化来加强,从而可以丢弃显然失败的配置。完全枚举方法是穷尽的,考虑了每一个可能性,没有优化。回溯法是一种更精细的方法,它在生成-测试范式的基础上增加了回溯的效率,显著减少了需要测试的配置数量。
“生成器更具信息化”指的是在生成可能的解决方案时,生成器具备了某种智能或知识,能够预先判断并排除那些显然不会成功的解决方案。这样,它就不会盲目地生成所有可能的解决方案,而是能够有选择性地只生成那些有可能成功的解决方案。
在解决问题的上下文中,一个“信息化”的生成器能够:
- 利用问题领域的知识:它了解问题的具体特征和规则,能够应用这些知识来避免创建一开始就违反问题约束的解决方案。
- 学习和适应:通过对之前尝试的结果进行分析,生成器能够学习哪些类型的解决方案可能会成功,哪些可能会失败,并调整其生成策略。
- 优化搜索过程:它能够识别那些已经探索过的无效或低效路径,并在未来的搜索中避免重复相同的错误。 例如,在解决n后问题时,一个信息化的生成器会知道放置一个后在已经由另一个后控制的行、列或对角线上是没有意义的,因此它不会生成这样的配置作为潜在的解决方案。这种方法可以大大减少需要测试的解决方案的数量,从而提高解决问题的效率。 2.2.2贪心算法 利用贪心算法(greedy algorithm),我们可以得到另外一种搜索方法。这种搜索方法也是先 将一个问题分成几个步骤进行求解,其中每次只考虑一个输入。贪心算法总是包含一个必须优 化(即最大化或最小化)的目标函数(objective function)。典型的目标函数可以是旅行距离、开 销或持续时间。 2.2.3旅行商问题 Dijkstra 的最短路径算法:
- 目的:查找图中两个节点之间的最短路径。
- 应用:在网络中用于确定源和目的地之间的最短路径,在地图应用中用于查找最短驾驶路线,以及在最短穿越路径很重要的各种其他领域。
- 过程:
- 从源节点开始,探索所有可到达的节点,根据路径成本从最近到最远扩展路径。
- 使用优先级队列来跟踪下一个最有希望探索的节点。
- 不断更新从源节点到每个节点沿最短路径的前驱节点到每个节点的最短路径距离。
- 优化:最小化从源节点到图中所有其他节点的总距离(或成本)。
- 图表类型:适用于没有负权重循环的加权图表。
- 结构:Dijkstra 算法在图结构上运行,使用加权边来表示节点之间移动的成本。 Dijkstra算法确实是贪心算法的典型代表,它在每一步都选择当前看起来最优的解决方案,即从当前节点到未访问节点中距离最短的节点。这种方法在求解最短路径问题时是有效的,因为路径的局部最优解(即最短边)通常能够导向全局最优解。 然而,对于旅行商问题(Travelling Salesman Problem, TSP),贪心算法通常不能保证找到全局最优解,原因如下:
- 局部最优与全局最优: 在旅行商问题中,贪心算法可能会选择当前距离最短的路径,但这个局部最优的选择可能会导致没有余地去访问其他节点,从而无法回到出发点或导致整体路径远非最优。
- 复杂性问题: TSP是一个NP完全问题,这意味着没有已知的多项式时间算法能够保证为所有可能的实例找到最优解。贪心算法虽然快速,但是它不能处理TSP的全局约束,即需要返回起点并且每个节点只能访问一次。
- 路径依赖性: 在TSP中,一旦选择了一条路径,就可能限制了后续的选择,这与最短路径问题不同,在最短路径问题中每次选择是独立的。
- 最优子结构的缺失: 贪心算法适用于具有最优子结构的问题,这意味着问题的最优解包含其子问题的最优解。然而,在TSP中,一个局部的最优路径选择不一定会导致全局的最优解。 举个例子,如果贪心算法在TSP中被使用,它可能会导致“近视” —— 优先选择最短的邻近边,最终可能绕过了一些远程节点,造成后续的路径非常长,从而使得整个回路的总长度远大于最优解。 因此,尽管贪心算法在某些问题上效率很高,但由于TSP的特殊性质和约束,贪心算法不能保证找到最优解。解决TSP通常需要使用启发式算法、回溯法或者分支限界法等更复杂的策略来尽可能找到接近最优解的解决方案。 分支定界(branch and bound)算法是广度优先搜索的一种变体。在这种搜索算法中,节点 按照开销不减少的原则进行探索。分支定界算法又称为统一代价搜索(uniform cost search),该 算法将在第 3 章中探讨,我们发现这种搜索策略可以成功解决旅行商问题的实例。 2.3盲目搜索算法 盲目搜索算法是不使用问题域知识的不知情搜索算法。这些算法假定不知道状 态空间的任何信息。3 种主要的盲目搜索算法如下:深度优先搜索(DFS)、广度优先搜索(BFS) 和迭代加深的深度优先搜索(DFS-ID)。这些算法都具有如下两个性质。 (1)它们不使用启发式估计。如果使用启发式估计,那么搜索将沿着最有希望得到解的路 径前进。 (2)它们的目标是找出给定问题的某个解。 之前学过的栈与队列可以用到了。 深度优先搜索(DFS)和广度优先搜索(BFS)可以看作是栈和队列这两种基本数据结构的高级应用。这两种搜索算法之所以区别于其他算法,很大程度上是因为它们如何使用栈和队列这两种数据结构来管理节点或状态的访问顺序。 2.3.1深度优先搜索 深度优先搜索(DFS),顾名思义,就是试图尽可能快地深入树中进行搜索。每当搜索方法 可以做出选择时,就选择最左(或最右)分支(通常选择最左分支)。 2.3.2广度优先搜索 广度优先搜索(BFS)是另一种盲目搜索算法。使用 BFS,从树的顶部到树的底部,按照从 左到右(或从右到左,不过从左到右更常见)的方式,可以逐层访问节点。 2.4盲目搜索算法的实现和比较 2.4.1深度优先搜索的实现 初始化:创建一个栈来存储访问路径,将起始节点压入栈中。 探索节点:从栈中取出一个节点作为当前节点。 访问邻居: 如果当前节点有未探索的邻居节点,选择其中一个,将其压入栈中,并标记为已访问。 如果当前节点没有未探索的邻居节点,将其从栈中弹出,进行回溯。 检查目标:每次访问一个节点时,检查它是否为目标节点。 重复:重复步骤2至4,直到栈为空或找到目标节点。 结束:当栈为空或找到目标节点时,算法结束。 2.4.2广度优先搜索的实现 初始化:创建一个队列来存储待访问的节点,将起始节点入队。 探索节点:从队列中取出一个节点作为当前节点。 访问邻居: 将当前节点的所有未探索邻居节点入队,并标记为已访问。 检查目标:每次访问一个节点时,检查它是否为目标节点。 重复:重复步骤2至4,直到队列为空或找到目标节点。 结束:当队列为空或找到目标节点时,算法结束。 2.4.3问题求解性能的衡量指标 完备 最优 时间复杂度 空间复杂度 2.4.4DFS和BFS的比较 深度优先搜索(DFS)和广度优先搜索(BFS)在搜索策略和解决问题的方式上有着本质的区别。 深度优先搜索(DFS):
- 搜索策略:从起点开始,沿着一条路径探索尽可能深,直到该路径的末端,然后回溯到之前的分叉点,探索另一条未被探索的路径。
- 内存占用:由于不需要存储所有的分支,DFS通常占用的内存较少。
- 找到解决方案的速度:如果解决方案位于深层,则可能会更快地找到;如果解决方案较浅,可能会较慢。
- 解决方案的优化性:DFS不保证找到最短路径的解决方案,它可能找到的是第一个遇到的解决方案。
- 实现:通常使用递归或者栈来实现。 广度优先搜索(BFS):
- 搜索策略:从起点开始,先探索所有邻近的节点,然后再对这些节点的邻近节点进行探索,以此类推,直到找到解决方案。
- 内存占用:BFS需要存储所有分层的分支,因此通常会占用更多的内存。
- 找到解决方案的速度:如果解决方案较浅,BFS将很快找到它,因为它逐层搜索。
- 解决方案的优化性:BFS保证找到的是最短路径的解决方案,因为它逐层扩展搜索。
- 实现:通常使用队列来实现。 在拼图问题中,DFS可能会迅速深入到拼图树的一个分支中,而BFS则会逐层扩展,更系统地探索每一种可能性。因此,对于找到最短路径解决方案的问题,BFS通常是更好的选择。而DFS在内存使用上更为节省,如果状态空间非常大或者解决方案处于深层,DFS可能表现更好。然而,对于拼图问题,特别是状态空间非常大的情况下(例如15-拼图问题),BFS的内存需求可能会非常高,因此也会考虑使用启发式搜索算法,如A*,它结合了DFS和BFS的优点,并且使用启发式信息来指导搜索方向。