美文网首页算法算法
算法思想:迭代加深

算法思想:迭代加深

作者: 5b56e393a2eb | 来源:发表于2018-12-20 20:22 被阅读145次

    废话不多说,我们从一个小例子开始。
    假定一个游戏,有一个2*4的棋盘,棋盘上有1到8的数字。每一步可以选择4种操作中的一种来改变棋盘。问从初始状态最终操作成一个给定的状态最少要几步。



    游戏的具体操作并不是我们讨论的主题,先略去。

    最朴素的想法

    搜索。我们用搜索树表示搜索的过程。一个结点就是棋盘的一个状态,结点的孩子就是经过操作后棋盘新的状态。对于棋盘的一个状态,每步可以做4种操作,也就是父节点有4个孩子。

    深搜
    对于棋盘的一个状态,由于每步可以做4种操作,我们递归的尝试每一种。就是基本的深搜。


    深搜的缺点很明显。如果答案状态所在的结点深度不大,那么搜到这个结点之前,对非常深的状态进行的搜索都是徒劳的。何况每一步都有4种选择,深度加深了时间呈指数级上升。

    广搜
    对上述的问题,广搜可以很好的解决。每步的四个操作同时加入队列,第一个到达目标状态的结点就是步数最少的。


    但广搜的问题也很明显。如果目标状态的深度稍微深一些,用于保存状态的队列所需的空间就会指数级增长。以本题为例,如果到达目标状态最少要10步,在第10层队列就需要保存4^10个,约1,000,000个结点。深度再深一些,分支再多一些,空间需求就会大的无法接受。

    迭代加深(iterative deepening)

    深搜广搜都有自己的缺点。迭代加深(以下简称ID)则结合两者的优点,用深搜模拟广搜
    ID不过是由以下两个思想启发产生。

    为了避免过大的空间需求,必须使用深搜。

    为了避免搜索无用且过深的搜索树,必须按广搜的模式控制搜索深度。

    控制深度的深搜,就是ID。
    深搜之前,我们设定一个限定的深度,要求深搜不得超过这个深度。
    每一次深搜,我们检查当前所处的深度。如果达到限定的深度,立刻返回。
    一次完整的深搜完成,如果没有找到答案,增加限定的深度,继续ID。


    很明显,无论深搜存在的搜索过深问题,还是广搜存在的空间问题,都能解决。

    现在,ID唯一令人怀疑的问题是:由于没有用空间保存信息,每次迭代都会把上一个深度的搜索树再次搜索一遍。这是否会让时间复杂度上升?
    我们进行一次证明。
    设D(x)是深度为x时搜索树内的结点数(0<=x)。节点的分支数,也就是每一步的可操作数,为k。可以看出,D(x)=1+k^{1}+k^{2}+……+k^{x}。等比数列,得D(x)=\frac{k^{x+1}-1}{k-1}
    如果答案的深度是h,那么如果只搜索深度为h的搜索树,也就是最好情况下的广搜,搜索的结点数是D(h)=\frac{k^{h+1}-1}{k-1}
    ID搜索的结点数则是:D(0)+D(1)+D(2)+……+D(h),等于

    \frac{k^{1}-1}{k-1}+\frac{k^{2}-1}{k-1}+\frac{k^{3}-1}{k-1}+……+\frac{k^{h}-1}{k-1}+\frac{k^{h+1}-1}{k-1}
    =\frac{\frac{k(k^{h}-1)}{k-1}-h}{k-1}+\frac{k^{h+1}-1}{k-1}
    =\frac{\frac{k^{h+1}-1}{k-1}-(h+1)}{k-1}+\frac{k^{h+1}-1}{k-1}
    =\frac{k}{k-1}\frac{k^{h+1}-1}{k-1}-\frac{h+1}{k-1}

    我们认为\frac{h+1}{k-1}这一项不影响复杂度,所以舍去。得原式等于:

    \frac{k}{k-1}\frac{k^{h+1}-1}{k-1}
    =\frac{k}{k-1} D(h)

    也就是说,ID搜索的结点数是广搜的\frac{k}{k-1}倍。
    情况最坏时k等于2(k不等于1,只有一个操作就不需要搜索了),ID的花的时间仅仅是最好的时间的2倍,属于常数范围。况且和广搜庞大的空间代价相比这点额外的时间简直是小巫见大巫。更何况当k更大,\frac{k}{k-1}更趋近于1,ID几乎没有浪费太多时间。
    证毕。

    伪代码

    最伟大的算法,代码永远是最简洁的。ID也不例外。

    IterativeDeepening()
    {
        /*max depth limited in DFS*/
        global maxdepth=1;
        while (answer hadn't been found)
        {
            dfs(0);
            maxdepth++; 
        }
        return answer;
    }
    
    dfs(int depth)
    {
        /*reach depth limit*/
        if (depth==maxdepth)    return;
        check whether answer is found;
        /*do dfs*/
        do dfs(depth+1);
    }
    
    最后

    ID思想之巧妙,一言难尽。实际应用很可能用其他方法剪枝,但ID的思想依然足够伟大。这里仅仅记录下一点心得,证明自己也有幸与这样的伟大思想碰撞。
    2018.12.20

    相关文章

      网友评论

        本文标题:算法思想:迭代加深

        本文链接:https://www.haomeiwen.com/subject/chaekqtx.html