第三章 通过搜索对问题求解
三个感叹!表重点
一. 问题求解Agent
- 搜索为一种求解问题的一般方法。
- 搜索问题:已知一个问题的初始状态和目标状态,找到一个操作序列,使得问题的状态能从初始状态转移到目标状态。
- 最优搜索问题:获得的操作序列不仅是问题的解,而且使得总代价最低。
- 搜索问题特点:
(1)初始状态确定
(2)当前状态是否为目标转态是可检测的
(3)状态空间离散
(4)每个状态可以采取的合法行动和相应后继状态是确定的
(5)环境是静态的
(6)路径代价函数是已知的 - 典序搜索问题
(1)八数码难题
(2)旅行售货员问题
- 搜索问题四个要素:
(1)初始状态
(2)后继函数:某个行动输入给定状态,可以输出相应的后继状态
(3)目标测试:给定状态是否为目标状态
(4)路径代价函数:状态转移所需的代价 - 搜索中需要解决的基本问题:
(1)是否一定能找到一个解。
(2)找到的解是否是最佳解。
(3)时间与空间复杂性如何。
(4)是否终止运行了或是否会陷入一个死循环。 - 搜索的主要过程:
(1) 从初始或目的状态出发,并将它作为当前状态。
(2) 扫描操作的算子集,将适用当前状态的一些操作算子作用于当前状态而得到新的状态,并建立指向其父结点的指针 。
(3)检查所生成的新状态是否满足结束状态,如果满足,则得到问题的一个解,并可沿着有关指针从结束状态反向到达开始状态,给出解答的路径;否则,将新状态作为当前状态,返回第(2)步再进行搜索。 - 搜索策略
盲目搜索 | 启发式搜索 |
---|---|
在不具有对特定问题的任何有关信息的条件下,按固定的步骤(依次或随机调用操作算子)进行的搜索。 | 考虑特定问题领域可应用的知识,动态地确定调用操作算子的步骤,优先选择较适合的操作算子,尽量减少不必要的搜索,以求尽快地到达结束状态。 |
- 按搜索方向进行分类:
(1) 数据驱动:从初始状态出发的正向搜索。
正向搜索——从问题给出的条件出发。
(2) 目的驱动:从目的状态出发的逆向搜索。
逆向搜索:从想达到的目的入手,看哪些操作算子能产生该目的,以及应用这些操作算子产生目的时需要哪些条件。
(3) 双向搜索:从开始状态出发作正向搜索,同时又从目的状态出发作逆向搜索,直到两条路径在中间的某处汇合为止。
二. 问题实例
- 实例: Romania
(1)已知条件:一个agent在罗马尼亚度假,目前位于Arad城市,Agent明天有航班从Bucharest起飞,不能改签退票
(2)
(3)真空吸尘器
三. 树搜索算法
- 基本思路:从初始状态/已知状态开始,通过行动不断地探索其他状态直到找到目标状态(成功)或者没有行动可执行为止(失败)。
- 树表示
(1)根节点:初始状态
(2)连线:行动
(3)结点:状态空间中的状态 - 算法性能评价标准
(1)完备性:如果问题有解时,这个算法是否能保证找到解?
(2)最优性:能否找到最优解
(3)时间复杂度:找到解需要花费多长时间
(4)空间复杂度:在执行搜索的过程中需要多少内存 - 时间空间复杂度的度量:
(1)时间由搜索过程中产生的结点数目来度量
(2)空间由内存中存储的最多结点数量来度量
(通常小于状态空间数量|V|+|E|)
四.无信息搜索策略(必考知识)
无信息搜索:除了问题定义中提供的状态信息外没有任何附加信息,算法只能区分状态是不是目标状态,无法比较非目标状态的好坏。
4.1 宽度优先搜索
- 思想:先扩展根结点,再扩展根结点的所有后继,然后再扩展它们的后继。
- 实现:FIFO队列
- 缺点:内存需求大,时间复杂度高
4.2 一致代价搜索 !!!
- 定义:扩展未扩展结点中代价最小的
- 实现:队列按照代价从小到大排列
4.3 深度优先搜索
- 思想:首先扩展最深的为扩展结点
- 实现:用LIFO队列(栈)来存储结点
4.4 深度受限搜索!!!
- 对于深度为d+1,分支数为b的情况,深度受限的搜索算法产生的结点数为:
N(DLS)= b0 + b1+…+ bd
4.5 迭代加深的深度优先搜索!!!
1.结合了宽度优先和深度优先的优点
2. 对于深度为d+1,分支数为b的情况,迭代加深的深度优先算法产生的结点数为:
N(IDS)=(d+1)+(d)b+(d-1)*b2+….+(1)bd
五. 有信息(启发式)的搜索策略
- 无信息搜索的缺点:
(1)广度优先搜索在进一步深入搜索之前先检查了靠近跟的节点,存储空间需求过高,很容易就被中等大小的分支因子给压垮了,例如分支因子=4(一个节点有四个孩子),则k层总共(4^k-1)/3个节点。
(2)深度优先搜索
(3)在求解"组合爆炸"的问题时,搜索空间增长太快(如从9!变成16!,!为阶乘),以至于盲目搜索方法无法成功。 - 启发法:一种解决问题的方法,这种方法通过尝试来证明结果,是**“凭经验"或"试错法”**的学习方式。
(1)是一个提高复杂问题解决效率的实用策略,它引导程序沿着一条最可能的路径到达解,忽略最没有希望的路径,能避免去检查死角。
(2)只使用已收集的数据。
(3)可以减少结点数目,适合组合复杂度快速增长的问题
(4)好的启发式方法不能保证获得解,但是它们经常有助于引导人们达到解的路径。
5.1 最佳优先搜索!!!
- 基本思路:通过对每一个结点计算评价函数f(n)值,找到一个f(n)最低的未扩散的结点
- 实现:在队列中,结点按照评价函数值从低到高排序。(大多数评价函数由启发函数h构成,h(n):结点n到目标结点的最小代价估计值)
- 最佳优先搜索实现需要open表和closed表,open表中节点按照节点接近目标状态的启发式估计值进行顺序排列,过程中不保留重复状态。
- 伪代码
//最佳优先搜索
BestFirstSearch(Root_Node,goal)
{
建open表,将根节点插入open表;
while(open表不为空)
{
从open表中取出最前节点放在节点G中(同时加入closed表);
if(G是目标节点) return 从根节点到G节点一条路径;
while(G是子节点)
{
if(子节点不在open表中) 将子节点插入open表;
else 将具有最小估计值的子节点放入open表,删除其他节点;
}
将open表中节点按值排序; //最小值节点在最前
}
return 失败;
}
- 分类:
贪婪最佳优先搜索 | 罗马尼亚问题 |
---|---|
首先扩展与目标结点估测距离最近的结点 | 使用两点之间的直线距离来估测两点之间的实际距离 |
- 罗马尼亚问题
- 例子:
5.2 一致代价搜索-分支定界法
- 是不采用剩余距离的启发式搜索,只计算已经走过的部分,h(n)处处为0。
- 按照递增的代价制定搜索路径,搜索的估计成本为:f(n)=g(n)
- 结束条件:其它路径(未到达目标节点)的代价
大于or等于当前最优的路径代价。 - 伪代码
//分支定界搜索
Branch_Bound(Root_Node,goal)
{
创建open表;
将根节点插入open表;
while(open表不为空)
{
取出open表中第一个元素放入结点G;
if(G是目标结点) return 到G的路径
else 将G结点子节点插入open表;
按照从根节点到当前节点的路径长度对open表排序;
}
return 失败;
}
- 例子:
- 一致代价搜索和最佳优先搜索都是通过启发函数排最优值,它们的区别如下
最佳优先搜索 | 一致代价搜索 |
---|---|
优先值为结点到终点的估计值 | 优先值为结点到起点的真实值 |
估价函数为h(n) | 估价函数为g(n) |
5.3 A搜索
- 是最佳搜索和一致优先的混合搜索算法,既考虑与源点的真实距离又兼顾与目标点距离的预估值。
- 评价函数
f(n) = g(n) + h(n)
g(n)=到达结点n已经花费的代价
h(n)=结点n到目标节点的评估代价
f(n)=通过结点n到达目标结点的总评估代价 - 伪代码
B_B_Estimata(Root_Node,goal)
{
创建open表;
将根节点插入open表;
while(open表不为空)
{
取出open表中第一个元素放入结点G;
if(G是目标结点) return 到G的路径
else 将每个子节点的估计距离加到当前距离;
将节点G的子节点插入open表
按照路径长度对open表排序;
}
return 失败;
}
- 例子
- 最佳优先搜索、一致搜索及A算法均****没有对估价函数f(n)做任何限制**
5.4 A*搜索
1.A*算法是最优A搜索算法,其是对估价函数加上一些限制后得到的一种启发式搜索算法。
2. 伪代码
A* Search(Root_Node,goal)
{
创建open表;
将根节点插入open表;
while(open表不为空)
{
取出open表中第一个元素放入结点G,并标记G已被访问;
if(G是目标结点) return 到G的路径
else 将每个子节点的估计距离加到当前距离;
将节点G的子节点中之前未访问过的节点插入open表
按照路径长度对open表排序;
}
return 失败;
}