极大极小搜索,即minimax搜索算法,专门用来做博弈论的问题的暴力.

多被称为对抗搜索算法.

这个搜索算法的基本思想就是分两层,一层是先手,记为a,还有一层是后手,记为b.

这个搜索是认为这a与b的利益关系是对立的,即假设a要是分数更大,b就要是分数更小.

而且这两个人都是用最优策略.

对,就是这样.

假设我们现在有一道题,给出一串数列,有两个选手按顺序选数,也就是一个选手选了ai,接下来另一个选手就必须选ai后面的任意一个数,然后每个选手选的数的累加和即为选手的分数,求先手比后手最多多几分.(两个选手都会选择最优策略)

保证序列里所有的数为正数.

那么我们可以设计一个算法:

先手的框架为:枚举上一次另一个选手选的数字后面开始选,取最大值.

后手的框架为:枚举上一次另一个选手选的数字后面开始选,取最小值.

即:

 1 int dfsb(int k){
 2   int minn=1000000000;
 3   for (int i=k;i<=n;i++)      //枚举接下来的要取的数 
 4     minn=min(minn,dfsa(i+1)-a[i]);      //搜索接下来对b最优的结果,也就是分数最小 
 5   minn=min(minn,0);      //考虑不取的情况 
 6   return minn;      //返回最优策略的得分 
 7 }
 8 int dfsa(int k){
 9   int maxx=-1000000000;
10   for (int i=k;i<=n;i++)      //枚举接下来的要取的数
11     maxx=max(maxx,a[i]+dfsb(i+1));      //搜索接下来对a最优的结果,也就是分数最大 
12   maxx=max(maxx,0);      //考虑不取的情况 
13   return maxx;      //返回最优策略的得分 
14 }

接下来是一个对于对抗搜索的最佳搭档——alpha-beta优化.

这个优化的思想很简单,即对于a来说,需要的是最大值,而下面的b取得是最小值.

而接下来如果b的已求出的最小值比a的已求出的最大值小,还有必要继续搜吗?

同样的,对于b来说,需要的是最小值,而下面的b取得是最大值.

于是这就是alpha-beta剪枝.

具体实现如下:

 1 int dfsb(int k,int maxx){      //多传一个maxx值表示上一层已经求出的最大值 
 2   int minn=1000000000;
 3   for (int i=k;i<=n;i++) {      //枚举接下来的要取的数 
 4     minn=min(minn,dfsa(i+1,minn)-a[i]);      //搜索接下来对b最优的结果,也就是分数最小 
 5     if (minn<maxx) return;      //alpha-beta优化 
 6   }
 7   minn=min(minn,0);      //考虑不取的情况 
 8   return minn;      //返回最优策略的得分 
 9 }
10 int dfsa(int k,int minn){      //多传一个minn值表示上一层已经求出的最小值 
11   int maxx=-1000000000;
12   for (int i=k;i<=n;i++) {      //枚举接下来的要取的数
13     maxx=max(maxx,a[i]+dfsb(i+1,maxx));      //搜索接下来对a最优的结果,也就是分数最大 
14     if (maxx>minn) return;      //alpha-beta优化 
15   }
16   maxx=max(maxx,0);      //考虑不取的情况 
17   return maxx;      //返回最优策略的得分 
18 }

 3 维基百科的伪码

如此的简洁~~~~~~

 1 function alphabeta(node, depth, α, β, Player)         
 2     if  depth = 0 or node is a terminal node
 3         return the heuristic value of node
 4     if  Player = MaxPlayer // 极大节点
 5         for each child of node // 极小节点
 6             α := max(α, alphabeta(child, depth-1, α, β, not(Player) ))   
 7             if β ≤ α // 该极大节点的值>=α>=β,该极大节点后面的搜索到的值肯定会大于β,因此不会被其上层的极小节点所选用了。对于根节点,β为正无穷
 8                 break                             (* Beta cut-off *)
 9         return α
10     else // 极小节点
11         for each child of node // 极大节点
12             β := min(β, alphabeta(child, depth-1, α, β, not(Player) )) // 极小节点
13             if β ≤ α // 该极大节点的值<=β<=α,该极小节点后面的搜索到的值肯定会小于α,因此不会被其上层的极大节点所选用了。对于根节点,α为负无穷
14                 break                             (* Alpha cut-off *)
15         return β 
16 (* Initial call *)
17 alphabeta(origin, depth, -infinity, +infinity, MaxPlayer)