zuoye

人工智能大作业( 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)
比较2
可以看出在空间很大的情况下, IDA 呈现出来的优势更加明显。但是当目标节点所处深度很深的时候 IDA 也是需要很大的工作量。

四、总结

以上我们可以看出在八数码问题中 IDA 算法在各个方面都要强于其他搜索算法。但 IDA 也并不是适用于所有搜索问题。我们知道 IDA 的缺点是对于一个层数较深的答案,要有很多重复的搜索,才能得到,并且会体现出深度优先搜索的复杂性,比如如果不加对八数码对无解性的判定,我们将很难判断一个答案的无解情况,因为 IDA 直到搜索完所有可能所在深度才能够判断无解,这个时候 IDA 将无异于深度优先搜索。
对于状态较少,其层数缺达到很高的问题,我们使用 IDA 算法将会有很多不必要的计算量。
具体问题还需具体分析,通常在 A 因为空间复杂度难以解决,并且最优解步数不会很多的时候,我们都可以考虑使用 IDA 的办法进行解决。

五、参考文献

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值