对抗搜索(二)

        在上一篇咱们分析了对抗搜索的基本思想,本文在上一篇的基础上对算法部分步骤进行优化。首先咱们看一个基于对抗搜索创建的博弈树,对上篇的内容作一个简单的回顾。(本篇内容可使用上篇开头说的两本书做为参考)java

        观察这颗博弈树,咱们可知它的固定深度是3,MAX先执行。换句话来讲就是,MAX为了决定第一步该走A、B仍是C,它要从根节点开始遍历深度为3的子树。再换句话来讲,这颗树其实只是为了得到MAX在第一步走的时候创建的博弈树;一样对于MIN方来讲,假如MAX第一步选择走A,那么以A节点为根节点,一样创建一颗深度为3的子树,经过遍历该子树,得到MIN在这一步的最佳选择,A一、A2或者A3(未画出该图)。算法

        通俗的说,对抗搜索其实就是在有限深度内枚举双发在每一步的选择,经过比较最终节点的状态(最大深度时)优劣,来得到此时执行方的最优选择。函数

        回归到原图,对MAX的选择进行一个详细的分析(请结合上一篇的MAX_VALUE和MIN_VALUE代码):优化

  1.  第一步由MAX方执行,假设此时的节点状态为R,R有三个选择,即三个后继节点--A、B、C;R要在这三个选择中选出“最利于获胜”的节点做为本身的执行步骤,按照极大极小值方法,即max(A,B,C),此时深度为0;
  2. 接下来轮到MIN方执行,它要在R选择的步骤基础上,选择利于本身的最优步骤,此时深度为1;
  3. 接下来轮到MAX方执行,它一样在MIN选择的步骤基础上,选择利于本身的最优步骤,此时深度为2;
  4. 接下来本应轮到MIN方执行,因为它已经到达最大深度3,再也不往下“递归”,即到达了该树的叶子节点,使用评价函数来计算当前叶子节点状态的值。能够看到每一个叶子节点都由一个评估值。
  5. 而后从叶子节点开始往上回溯,看到在深度2时,MAX方在每个选择下的最优选择;
  6. 继续往上回溯,此时回溯到MIN方的选择;
  7. 最终咱们回溯到最初MAX此时的最优选择,选择C做为这一步的选择;

        接下来咱们思考一个问题,咱们能够看到上述的算法是对全部的候选项都进行枚举遍历比较,这样若是深度比较大时,好比固定截取深度为10,那么对每一步来讲都要计算最大深度为10的子树,这样的计算量会很是大。有没有可能缩小比较范围?spa

        首先咱们要清楚,max_value和min_value方法以DFS(深度优先)进行遍历。接下来咱们对以A节点为根节点的子树进行详细分析。首先从深度3开始往上回溯时,此时A1的最优选择为6;此时再往上回溯,A节点此时的最优选择为6;而后开始遍历A2节点,由于A节点此时的值是6,若是A2节点可以获得该值的信息的话,那么在以A2节点为根的深度遍历时,遇到比6大的值,其实能够放弃该子树的后续遍历了。缘由就是A2节点是MAX方来执行,若是A2的子节点有比6大的,那么MAX方确定会选择比6大的值,而A节点是MIN方来执行,它要选择的是A一、A二、A3的最小值,因此此时A节点确定不会选择A2做为最优选择。code

        一样,在A的选择结束后,向上回溯到R,此时R的最优选择是6,因为R要选取最大值,那么在对剩余以B和C为根节点进行深度遍历时,若是有遇到比6小的值,那么该节点确定不是最优的选择。缘由就是MAX方要选择后继节点中的最大值,比6小的话,确定不是最优选择。blog

        上述方法就是树结构最经常使用的优化方法--剪枝,这里的优化方法叫作Alpha-Beta方法。下面是经过Alpha-Beta剪枝方法对max_value方法和min_value方法的改进。递归

int max_value ( int dep , state s , int alpha , int beta ){
    if ( terminal ( s )) return e ( s ); //终止状态
    if ( dep == maxdepth ) return e ( s ); //深度截断,返回评价函数
    v = - inf ; //初始化为负无穷
    succ = make_successors ( s ); // succ [ i为第]个后继状态i
    for ( i = 0; i < succ . count ; i ++){
        v = max (v , min_value ( succ [ i ] , alpha , beta )); //计算全部儿子的最大值
        if ( v >= beta ) return v ; //β剪枝
        alpha = max ( alpha , v ); //更新α为最大值
    }
    return v ;
}
int min_value ( int dep , state s, int alpha, int beta){
    if ( terminal ( s )) return e ( s ); //终止状态
    if ( dep == maxdepth ) return e ( s ); //深度截断,返回评价函数
    v = inf ; //初始化为无穷大
    succ = make_successors ( s ); // succ [ i为第]个后继状态i
    for ( i = 0; i < succ . count ; i ++){
    v = min (v , max_value ( succ [ i ] , alpha , beta )); //计算全部儿子的最小值
        if ( v <= alpha ) return v ; //α剪枝
        beta = min ( beta , v ); //更新β为最小值
    }
    return v ;
}

         接下来咱们按照改进后的max_value方法和min_value方法对上述的博弈树进行剪枝,方便你们理解。terminal

  1. 初始状态,alpha=-inf, beta=inf (inf表明最大值);经过深度优先遍历向下传递alpha, beta, 在到达叶子节点时,由于是计算评价函数alpha和beta其实没有用;而后开始向上回溯到A1节点;
  2. A1节点回溯前alpha=-inf, beta=inf,回溯后变为alpha=6, beta=inf;向上回溯到A节点;
  3. A节点回溯前alpha=-inf,beta=inf, 回溯后变为alpha=-inf, beta=6; 向下递归遍历A2节点;
  4. A2节点此时alpha=-inf, beta=6; 继续向下递归遍历,咱们知道A2是max方来执行,此时调用的是max_value方法,而且有三个后继节点;
    1. 遍历第一个节点时,2<6, alpha = 2, beta=6;
    2. 遍历第二个节点时,8>6, 此时直接返回,也就是第三个节点再也不遍历,由于max此时的选择确定是大于6的,对A节点来讲,A2节点确定不是最优的选择;
    3. 此时向上回溯到A节点;
  5. 一样对于A3节点来讲,在遍历完第一个节点7时,直接返回,继续回溯到A节点;
  6. 此时A节点的alpha = -inf, beta=6; 此时A节点向上回溯到R节点;
  7. R节点回溯前alpha=-inf,beta=inf; 回溯后变为alpha=6, beta=inf; R节点向下深度遍历到B节点;
  8. B节点alpha=6, beta=inf; 继续向下递归到B1节点;
  9. B1节点alpha=6, beta=inf; 继续向下递归遍历三个后继节点,三个节点的值都比beta小,因此只是在每一个节点内部更改了alpha;最终回溯到B1节点;
  10. B1回溯前alpha=6, beta=inf, 回溯后alpha = 6, beta = inf; 接着向上回溯到B节点;
  11. B节点回溯前alpha=6, beta=inf, 此时回溯的值是5, 5<alpha=6, 直接回溯到R节点,也就是说直接裁剪了B2和B3节点值。缘由: 由于B节点是由MIN方来执行,它要选择最小的节点做为最佳选择,因此不论B2和B3是什么值,对于B来讲最佳选择确定是小于等于5;而R是选择最大值,此时,R此时最优的选择是6,确定不会选择B节点做为最优选择,因此对于B2和B3来讲,已经没有继续搜索的必要了;接着向上回溯到R节点;
  12. R节点回溯前alpha=6, beta=inf; 向下递归遍历C节点;
  13. C节点alpha=6, beta=inf, 继续向下递归遍历C1;
  14. C1节点alpha=6, beta=inf, 继续向下递归遍历叶子节点, 因为都小于beta, 因此依然只是比较alpha的值,获取最大值向上回溯到C节点;
  15. C节点回溯前alpha=6,beta=inf, 回溯后alpha = 6, beta=7; 向下递归遍历C2节点;
  16. C2节点alpha=6,beta=7, 向下递归遍历叶子节点;
    1. 遍历第一个叶子节点4, 小于beta;
    2. 遍历第二个叶子节点8, 大于beta, 此时直接返回,裁剪了叶子节点1,直接向上回溯到节点C节点;
  17. C节点回溯前alpha = 6, beta=7;回溯后返回值为8大于beta, 依然选取最小值,alpha=6, beta=7;
  18. 对于C3节点与C2的处理方式相同,C3向上回溯到C节点;
  19. C节点回溯前alpha=6,beta=7,回溯后alpha=6,beta=7, 并选择最小值为7回溯到R节点;
  20. R节点回溯前alpha=6,beta=inf,回溯后alpha=7,beta=inf;

    最终的裁剪图以下,标红的表明它的子树都被裁剪了。class

总结

        alpha表明的是MAX方选择的最小下界;

        beta表明的是MIN方选择的最大上界;

相关文章
相关标签/搜索