人工智能大作业( yu )
标签(空格分隔): 作业
IDA∗ 算法的探究
一、盲目搜索到 A∗
盲目搜索:又叫做无信息搜索。通常有深度优先搜索和广度优先搜索。
广度优先搜索空间成指数形增长,很难搜索一些较大的搜索图。
深度优先虽然可以在搜索过程中,只保留一部分搜索树,使我们搜足够的空间对图进行搜索。但是深度优先搜索当搜到目标节点并不能保证这是最优的路径,要保证找到最优解就要遍历完全图(耗时)。而且即使目标节点位于一个深度不高的位置,我们也有可能搜索大部分的图。
迭代加深搜索虽然能够保留深度优先搜索对空间的优势,也能够解决深度优先搜索对深度不高的目标节点的缺陷,但在目标处于深层时仍然无能为力。
A∗
算法:启发式搜索。它会沿着有启发性和具有特定信息的节点搜索下去,沿着图中最有可能在最佳路径上的点搜索下去。依据评价函数
ĥ
,尽量避开一些无用的搜索分支,较快的找到目标节点。
我们知道广度优先搜索中对空间的需求随着深度的增加呈指数增长。尽管
A∗
利用好的评价函数,减少了一些无用的空间消耗,除此之外,对
OPEN
表和
CLOSED
表的维护也耗费了时间。但大型问题中,依旧会有如上问题。
二、 IDA∗ (迭代加深的 A∗ 搜索)
为了解决
A∗
算法中仍然存留的空间消耗问题,而解决空间问题的最好方法就是深度优先搜索。于是我们用类似迭代加深搜索的方法,对
A∗
进行改进。
IDA∗
算法:在
IDA∗
算法中,我们为
f̂
建立一个阈值,定义节点可以被评估的最大允许花费,所有节点都在这个阈值下进行深度优先扩展。如果目标节点没有在这个阈值下被搜索到,阈值便会增加。由于我们没有维护搜索历史,就需要从七点重新搜索。
直观上,所有之前的非目标节点都会被重复搜索。但与
A∗
搜索中
OPEN
表和
CLOSDE
表的存储和排序,判重维护相比,效率时常高于
A∗
搜索。
基于栈算法框架:
Procedure IDA_STAR(StartState)
Begin
PathLimit := H( StartState) - 1;
Success := False
Repeat
inc(PathLimit);
StartState.h := 0
Push(OpenStack,StartState);
Repeat
CurrentState := Pop(OpenStack);
if Solution(CurrentState) then
Success = True
Else if PathLimit >= CurrentState.h + H(CurrentState) then
Foreach Child(CurrentState) do
Push(OpenStack,Child(CurrentState));
until Success or empty(OpenStack);
until Success or ResourceLimitsReached
end;
三、实例
这里使用八数码问题和十六数码问题对 IDA∗ 算法进行探究。
实例一:八数码问题
算法的实现
1、八数码的状态:
struct Eight{
//x,y表示x所在坐标 g数组表示八数码的状态(包含x共九个数) h则为启发函数
int g[N+1],x,y,h;
void geth(){
h=0;
for(int i=0;i<=N;i++){
int x1=i/3,y1=i%3;
int x2=(g[i]-1)/3,y2=(g[i]-1)%3;
h+=abs(x1-x2)+abs(y1-y2);
}
}
void print(){
for(int i=0;i<=N;i++) printf("%d ",g[i]);
puts("");
}
}st,en;
2、判定终态
bool have_solution(int *a){
int cnt=0;
for(int i=0;i<=N;i++)if(a[i]!=9){
for(int j=i+1;j<=N;j++)if(a[j]!=9){
if(a[i]>a[j]) cnt++;
}
}
return cnt%2==0;
}
和终态相同则返回true 否则返回false
3、
IDA_Star()
函数
初始判断是否有解,无解直接输出unsolvable返回。否则进行
IDA∗
算法,flag表示算法是否已经找到解,停止循环的条件为: (1)找到合法解 (2)阈值不再变大。
void IDA_Star(){
flag=0;
f_limit=st.h;
if(!have_solution(st.g)){
printf("unsolvable\n");
return ;
}
while(!flag){
nt_limit=dfs(0,st);
if(f_limit==nt_limit) break;
f_limit=nt_limit;
}
if(flag){
for(int i=0;i<f_limit;i++) printf("%c",dis[ans[i]]);
puts("");
}
else printf("unsolvable\n");
}
4、
DFS
函数(深度优先搜索)
算法要求每次深度优先搜索返回未被搜到的最小
f̂
值。一旦flag被置为1(即发现解)则立即退出。
int dfs(int dev,Eight cur){
cur.geth();
if(dev+cur.h>f_limit){
return dev+cur.h;
}
if(cur.h==0){
flag=1;
return dev;
}
int now=1e9,x=cur.x,y=cur.y,id=x*3+y;
for(int i=0;i<4;i++)if(!dev||(ans[dev-1]+i)%4!=1){
int nx=x+d[i][0];
int ny=y+d[i][1];
if(inmap(nx,ny)){
int nid=nx*3+ny;
swap(cur.g[id],cur.g[nid]);
cur.x=nx;
cur.y=ny;
ans[dev]=i;
now=min(now,dfs(dev+1,cur));
if(flag) return now;
swap(cur.g[id],cur.g[nid]);
}
}
return now;
}
算法的分析
分别使用传统的广度优先算法和迭代加深搜索,启发式搜索
A∗
和
IDA∗
算法对八数码问题进行求解。
对算法的补充说明:
这里的启发函数选择:除可移动点之外其他数字到正确位置的曼哈顿距离的和作为
A∗
和
IDA∗
的启发式函数。
并在广度优先搜索和
A∗
搜索时,使用哈希表对其进行空间和时间的优化。根据启发式函数的选择,在
A∗
搜索中我们不对一个点进行重定向。
对所有算法加上无解特判。
对比:三个方面
1、时间复杂度的对比:
在广度优先搜索和
A∗
的程序中加入了哈希表的优化,结果广度优先搜索完全慢于
A∗
和
IDA∗
,而
A∗
稍逊色于
IDA∗
。而迭代加深搜索除了能够找到深度很小的情况。其他情况基本上不能够完成任务。可见
IDA∗
算法的好处。
2、空间复杂度的对比:
很显然的
A∗
和
BFS
中,需要保存
OPEN
表结构即状态,而8数码问题一共将产生
9!=362880
个状态,可见状态空间是极比较的。而在
IDA∗
中我们使用深度优先搜索。并没有保存状态,状态数依据深度成线性增加。如果说时间上
A∗
和
IDA∗
,那么在空间问题上
A∗
完全不能够跟
IDA∗
作比较。
3、编程复杂度的对比:
A∗
以广度优先作为基础,
IDA∗
和迭代加深都是使用深度优先作为基础。
A∗
算法则更是需要完成对
OPEN
和
CLOSED
表进行维护。对
OPEN
表我们需要维护其最小值,使用单调队列,
CLOSED
表使用哈希散列。
IDA∗
则只需要一个深度优先搜索和对
limit
的循环。编程复杂度也是
IDA∗
更低。
实例二:十六数码
十六数码问题如同八数码类似。比八数码多了一行一列。多了这一行一列就多了
16!=20922789888000=2e19
个状态。显然的
A∗
对这些状态无从下手,单单是维护
OPEN
和
CLOSED
表就会很费时间。并且即便选择了比较好的启发函数,也会造成严重的空间问题。
IDA∗
算法实现起来如同八数码一样。在这个问题中我们只将
IDA∗
和
A∗
做对比。
算法的实现:
实现是很简单的,对八数码进行一些修改即可。
只是十五数码和八数码判断是否有解的方法不同,我们进行一下简单的说明,八数码9的移动不影响其余7个数字逆序数的奇偶性,而十五数码9的左右移动不影响其余15个数逆序数的奇偶性(顺序不变),但上下移动改变奇偶(移动三次),加上9的话16个数逆序数左右改变(移动一次),上下也改变(移动7次),需要注意每次移动9的距离奇偶性也改变(9到目标位置的曼哈顿距离不是加1就是减一),所以16个数逆序数与9的距离之和的奇偶性不因9的滑动而改变,初始时是偶数,所以只有是偶数的状态才是可到达的。
bool have_solution(int *state){
int cnt=0;
for(int i=0;i<=N;i++){
for(int j=i+1;j<=N;j++){
if(state[i]>state[j]) cnt++;
}
if(state[i]==16) cnt+=abs(i/4-3)+abs(i%4-3);
}
return cnt%2==0;
}
算法的分析:
与
A∗
算法进行对比。
对
A∗
算法的补充说明:
采用曼哈顿距离作为估价函数。
状态空间太大,不再能够使用合适的Hash优化。这里使用c++中的map保存状态的信息。一次操作复杂度
O(log)
。
可以看出在空间很大的情况下,
IDA∗
呈现出来的优势更加明显。但是当目标节点所处深度很深的时候
IDA∗
也是需要很大的工作量。
四、总结
以上我们可以看出在八数码问题中
IDA∗
算法在各个方面都要强于其他搜索算法。但
IDA∗
也并不是适用于所有搜索问题。我们知道
IDA∗
的缺点是对于一个层数较深的答案,要有很多重复的搜索,才能得到,并且会体现出深度优先搜索的复杂性,比如如果不加对八数码对无解性的判定,我们将很难判断一个答案的无解情况,因为
IDA∗
直到搜索完所有可能所在深度才能够判断无解,这个时候
IDA∗
将无异于深度优先搜索。
对于状态较少,其层数缺达到很高的问题,我们使用
IDA∗
算法将会有很多不必要的计算量。
具体问题还需具体分析,通常在
A∗
因为空间复杂度难以解决,并且最优解步数不会很多的时候,我们都可以考虑使用
IDA∗
的办法进行解决。