α-β 剪枝课件
- 格式:ppt
- 大小:8.26 MB
- 文档页数:19
AlphaBeta算法是根据Minimax算法得来的,首先我们必须明白MiniMax算法的思想。
Minimax算法常用于棋类等由两方较量的游戏和程序。
该算法是一个零总和算法,即一方要在可选的选项中选择将其优势最大化的选择,另一方则选择令对手优势最小化的方法。
而开始的时候总和为0。
但是如果实际中使用Minimax算法,由于搜索深度和可能的情况很多,算法的效率很不理想,其实并没有必要每个节点都必须搜索完毕,有些事没有必要的。
AlphaBeta算法正是为了解决这个问题。
1. 对于一个MIN节点,若能估计出其倒推值的上确界Beta,并且这个Beta值不大于MIN 的父节点(MAX节点)的估计倒推值的下确界Alpha,即Alpha≥Beta,则就不必再扩展该MIN 节点的其余子节点了,因为这些节点的估值对MIN父节点的倒推值已无任何影响了,这一过程称为Alpha剪枝。
2. 对于一个MAX节点,若能估计出其倒推值的下确界Alpha,并且这个Alpha值不小于MAX 的父节点(MIN节点)的估计倒推值的上确界Beta,即Alpha≥Beta,则就不必再扩展该MAX 节点的其余子节点了,因为这些节点的估值对MAX父节点的倒推值已无任何影响了。
这一过程称为Beta剪枝。
3. 一个MAX节点的Alpha值等于其后继节点当前最大的最终倒推值,一个MIN节点的Beta 值等于其后继节点当前最小的最终倒推值AlphaBeta剪枝算法AlphaBeta剪枝算法(假设方框表示取极大值的节点,圆圈表示取极小值的节点)B的值是18,D的值为16,而C是取极小值,由此可以判断C《=16,而A取Max(B,C),故没必要考虑C的其他子节点了。
Alphabeta的MiniMax形式,伪代码:alpha-beta(player,board,alpha,beta)if(game over in current board position) return winnerchildren = all legal moves for player from this boardif(max's turn)for each childscore = alpha-beta(other player,child,alpha,beta)(we have found a better best move....)if score > alpha then alpha = score(cut off...)if alpha >= beta then return alphareturn alpha (this is our best move)else (min's turn)for each childscore = alpha-beta(other player,child,alpha,beta)(opponent has found a better worse move.....)if score < beta then beta = score(cut off....)if alpha >= beta then return betareturn beta (this is the opponent's best move)AlphaBeta的递归形式01 int AlphaBeta(int depth, int alpha, int beta)02 {//如果层数为0或者已达最终状态则返回本步棋的估值03 if(depth == 0 || IsGameOver()) return Evaluate();04 for(each possible move){06 MakeMove();08 int val = -AlphaBeta(depth - 1, -beta, -alpha);09 UnMakeMove();11 if(val >= beta){13 return val;14 //注意,这里需要返回val,因为上一层应该知道具体搜索到的值,以配合各种Alpha-Beta算法的变种15 }16 if(val > alpha){18 alpha = val;19 ...20 //当然这里还需要记录这步最佳的走法21 }24 }25 return alpha;//返回最好的值26 }2728 Alpha表示MAX节点的下界值,29AlphaBeta算法的递归形式关键就是理解负号,在每一层节点中都是求最大值(例如CP-OP 的最大值,将A方的最大值返回给B,即最小化B的利益),然后返回给父节点的时候加个负号得到的就是(OP-CP),也就是双方都为对方找最差的走法。
人工智能期中作业一字棋编程姓名:班级:学号:一、程序设计思想:1.通过判断一字棋的棋局是否与之前搜索过的棋局重复来减小搜索的复杂度。
(通过对称属性来判断是否重复)2.主程序采用递归的思想来解决此类复杂问题。
主程序的功能为按照αβ剪枝策略算出当前棋局的分数,依次递归。
int jianzhi(enzo a,int tier)为整个程序的关键函数。
其中enzo 是结构体类型(自定义),int tier 为层数。
递归如下:v[tier]=max(jianzhi(a,tier+1),v[tier]);(其中a每次传递之前都会被更新)。
3.如何判断是否是αβ剪枝是关键。
先用int v[4]数组来存储第0 、1、2、3层的分数。
初始值分别为-100,100,-100,100。
共有3种α剪枝情况和1中β剪枝情况。
详情见Int aorb();子函数。
二、程序源代码:#include <iostream>#include<vector>using namespace std;int jzs=0;int ajz=0,bjz=0;int v[4]= {-100,100,-100,100};class enzo{public:int a[3][3];//棋局enzo()//初始构造函数{for(int i=0; i<3; i++)for(int j=0; j<3; j++)a[i][j]=2;}void pr()//输出棋局{for(int i=0; i<3; i++){for(int j=0; j<3; j++){if(a[i][j]==1) cout<<'X'<<" ";if(a[i][j]==0) cout<<'O'<<" ";if(a[i][j]==2) cout<<". ";}cout<<endl;}}};//计算数组的静态估值int value_1(enzo a,int b){int v=0;for(int i=0; i<3; i++){for(int j=0; j<3; j++)if(a.a[i][j]==2) a.a[i][j]=b;}// a.pr();for(int i=0; i<3; i++)if(a.a[i][0]==b&&a.a[i][1]==b&&a.a[i][2]==b) v++;for(int i=0; i<3; i++)if(a.a[0][i]==b&&a.a[1][i]==b&&a.a[2][i]==b) v++;if(a.a[0][0]==b&&a.a[1][1]==b&&a.a[2][2]==b) v++;if(a.a[0][2]==b&&a.a[1][1]==b&&a.a[2][0]==b) v++;return v;}int value(enzo a){return(value_1(a,1)-value_1(a,0));}bool sym(enzo a,enzo b)//判断是否上下左右斜对称(没有考虑旋转的情况){if(a.a[0][1]==b.a[0][1]&&a.a[1][1]==b.a[1][1]&&a.a[2][1]==b.a[2][1]) //左右对称if(a.a[0][0]==b.a[0][2]&&a.a[1][0]==b.a[1][2]&&a.a[2][0]==b.a[2][2])if(a.a[0][2]==b.a[0][0]&&a.a[1][2]==b.a[1][0]&&a.a[2][2]==b.a[2][0]) return true;if(a.a[1][0]==b.a[1][0]&&a.a[1][1]==b.a[1][1]&&a.a[1][2]==b.a[1][2]) //上下对称if(a.a[0][0]==b.a[2][0]&&a.a[0][1]==b.a[2][1]&&a.a[0][2]==b.a[2][2])if(a.a[2][0]==b.a[0][0]&&a.a[2][1]==b.a[0][1]&&a.a[2][2]==b.a[0][2]) return true;if(a.a[0][0]==b.a[0][0]&&a.a[1][1]==b.a[1][1]&&a.a[2][2]==b.a[2][2]) //两个斜对称if(a.a[0][1]==b.a[1][0]&&a.a[0][2]==b.a[2][0]&&a.a[1][2]==b.a[2][1])if(a.a[1][0]==b.a[0][1]&&a.a[2][0]==b.a[0][2]&&a.a[2][1]==b.a[1][2]) return true;if(a.a[0][2]==b.a[0][2]&&a.a[1][1]==b.a[1][1]&&a.a[2][0]==b.a[2][0])if(a.a[0][0]==b.a[2][2]&&a.a[0][1]==b.a[1][2]&&a.a[1][0]==b.a[2][1])if(a.a[2][2]==b.a[0][0]&&a.a[1][2]==b.a[0][1]&&a.a[2][1]==b.a[1][0]) return true;return false;}bool nsym(enzo a,enzo b){if(sym(a,b)) return false;else return true;}int aorb()//a - 0 b -1{if(v[0]>=v[1]&&v[0]!=-100&&v[1]!=100){jzs++;cout<<jzs<<": "<<"发生a剪枝"<<endl;ajz++;return 1;}else if(v[0]>=v[3]&&v[0]!=-100&&v[3]!=100){jzs++;cout<<jzs<<": "<<"发生a剪枝"<<endl;ajz++;return 1;}else if(v[2]>=v[3]&&v[2]!=-100&&v[3]!=100){jzs++;cout<<jzs<<": "<<"发生a剪枝"<<endl;ajz++;return 1;}else if(v[1]<=v[2]&&v[1]!=100&&v[2]!=-100){jzs++;cout<<jzs<<": "<<"发生b剪枝"<<endl;bjz++;return 1;}else return 0;}int jianzhi(enzo a,int tier){//a.pr();if(tier==4) return value(a);if(tier%2)//极小层{vector<enzo> hi;for(int i=0; i<3; i++){for(int j=0; j<3; j++){if(a.a[i][j]==2){int u=0;int qq=0;a.a[i][j]=0;for(u=0; u<(int)hi.size(); u++){if(sym(hi[u],a)) break;}if((int)hi.size()==u){hi.push_back(a);v[tier]=min(jianzhi(a,tier+1),v[tier]); if(aorb()) qq=1;}a.a[i][j]=2;if(qq==1){a.pr();cout<<endl;v[tier]=100;return -100;}}}}int hj=v[tier];v[tier]=100;return hj;}Else//极大层{vector<enzo> hi;for(int i=0; i<3; i++){for(int j=0; j<3; j++){if(a.a[i][j]==2){int u=0;int qq=0;a.a[i][j]=1;for(u=0; u<(int)hi.size(); u++){if(sym(hi[u],a)) break;}if((int)hi.size()==u){hi.push_back(a);v[tier]=max(jianzhi(a,tier+1),v[tier]); if(aorb()) qq=1;}a.a[i][j]=2;if(qq==1){a.pr();v[tier]=-100;return 100;}}}}int hj=v[tier];v[tier]=-100;return hj;}}int main(){enzo a0;jianzhi(a0,0);cout<<"一共"<<ajz<<"次a剪枝"<<endl;cout<<"一共"<<bjz<<"次b剪枝"<<endl;}三、αβ剪枝搜索过程(其中’.’表示空)共发生了23次α剪枝,5次β剪枝。
5. Adversarial SearchContents:☐5.1. Games☐5.2. Optimal Decisions in Games☐5.3. Alpha-Beta Pruning☐5.4. Imperfect Real-time Decisions☐5.5. Stochastic Games☐5.6. Monte-Carlo Methods☐The problem with minimax search: 采用minimax搜索的问题:⏹Number of game states is exponential in depth of the tree.博弈状态的量随着树的深度呈现指数式增长。
⏹We can’t eliminate the exponent, but we can effectively cut it in half.我们无法消除这种指数,但实际上我们可以将它剪掉一半。
☐The trick to solve the problem: 解决该问题的技巧⏹Compute correct minimax decision without looking at every node in game tree.计算正确的minimax决策而不考虑博弈树的每个节点。
⏹That is, use“pruning” to eliminate large parts of the tree.就是说,采用“剪枝”方法来消除该树的大部分。
☐What is alpha–beta pruning 什么是alpha–beta剪枝⏹It is a search algorithm that seeks to decrease the number of nodes that areevaluated by the minimax algorithm.是一种搜索算法,旨在削减由minimax算法评价的节点数量。
AlphaBeta剪枝算法关于AlphaBeta剪枝的⽂章太多,这个⽅法是所有其它搜索⽅法的基础,得多花些时间认真地理解。
先把基本概念再回顾⼀遍:节点:在中国象棋中就是⼀个棋盘的当前局⾯Board,当然该轮到谁⾛棋也是确定的。
这⾥的圆形节点表⽰终⽌节点,在中国象棋⾥就是⼀⽅被将死的情况(或者到达了搜索的最⼤深度),后续不会再有着法产⽣,游戏如果⾛到这⾥就会结束。
在引擎⾥通常给红⽅⼀个很⼤的评估值,如+30000,给⿊⽅⼀个很⼩的评估值,如-30000,来⽅便地判断这种结束局⾯。
(胜利局⾯有稍微不同的表⽰法,⽤-30000+层数ply来表⽰)连线:表⽰⼀步着法Move,通过这步着法后,局⾯发⽣变化,先后⼿也要交换。
层:通常的术语是ply,复数形式是plies,也有称为levels,当然与depth也是对应的。
这个术语是为了与⽐赛⾥常说的回合相区分,⼀个回合通常包含2步,这个ply就表⽰某⼀⽅⾛了⼀步。
根节点记为0层,以下的层数递增。
深度depth:要注意是从下到上的,还是从上到下的。
(1)通常的算法书中都是从下到上递增的,即根节点为最⼤搜索深度,⾛到最底部的叶⼦结点为0,这种算法只要记住⼀个depth值就可以了。
(2)⽽另⼀种记法是根部结点为0,越向下depth增加,这时在搜索时就要传递2个参数值,depth和maxDepth,稍微有点啰嗦,应该也会影响⼀点效率。
另外在探查置换表中的结点时,⽤第(1)种记法也⽅便⼀些,因为要知道从当前节点迭代的深度值,否则还要在置换表中保存depth和maxDepth两个值。
AlphaBeta剪枝⽅法是对Minimax⽅法的优化,它们产⽣的结果是完全相同的,只不过运⾏效率不⼀样。
这种⽅法的前提假设与Minimax也是⼀样的:1)双⽅都按⾃⼰认为的最佳着法⾏棋。
2)对给定的盘⾯⽤⼀个分值来评估,这个评估值永远是从⼀⽅(搜索程序)来评价的,红⽅有利时给⼀个正数,⿊⽅有利时给⼀个负数。
组合游戏1:详解Minimax和AlphaBeta剪枝算法本系列,我们来看看在⼀种常见的组合游戏——回合制棋盘类游戏中,如何⽤算法来解决问题。
⾸先,我们会介绍并解决搜索空间较⼩的问题,引⼊经典的博弈算法和相关理论,最终实现在⼤搜索空间中的Deep RL近似算法。
在此基础上可以理解AlphaGo的原理和⼯作⽅式。
本系列的第⼀篇,我们介绍3个Leetcode中的零和回合制游戏,从最初的暴⼒解法,到动态规划最终演变成博弈论⾥的经典算法:minimax 以及 alpha beta 剪枝。
获得最好的阅读体验,请点击最下⽅阅读原⽂,并在电脑上打开第⼀篇 [Leetcode中的Minimax 和 Alpha Beta剪枝]第⼆篇: ⼀些组合游戏的理论第三篇: 连接N个点的OpenAI Gym GUI环境第四篇: 蒙特卡洛树搜索(MCTS)和时间差分学习(TD learning)Leetcode 292 Nim Game (简单)简单题 Leetcode 292 Nim Game。
你和你的朋友,两个⼈⼀起玩 Nim游戏:桌⼦上有⼀堆⽯头,每次你们轮流拿掉 1 - 3 块⽯头。
拿掉最后⼀块⽯头的⼈就是获胜者。
你作为先⼿。
你们是聪明⼈,每⼀步都是最优解。
编写⼀个函数,来判断你是否可以在给定⽯头数量的情况下赢得游戏。
⽰例:输⼊: 4输出: false解释: 如果堆中有 4 块⽯头,那么你永远不会赢得⽐赛;因为⽆论你拿⾛ 1 块、2 块还是 3 块⽯头,最后⼀块⽯头总是会被你的朋友拿⾛。
定义为有个⽯头并采取最优策略的游戏结果,的值只有可能是赢或者输。
考察前⼏个结果:,然后来计算。
因为玩家采取最优策略(只要有⼀种⾛法让对⽅必输,玩家获胜),对于4来说,玩家能⾛的可能是拿掉1块、2块或3块,但是⽆论剩余何种局⾯,对⽅都是必赢,因此,4就是必输。
总的说来,递归关系如下:这个递归式可以直接翻译成Python 3代码# TLE# Time Complexity: O(exponential)class Solution_BruteForce:def canWinNim(self, n: int) -> bool:if n <= 3:return Truefor i in range(1, 4):if not self.canWinNim(n - i):return Truereturn False以上的递归公式和代码很像fibonacci数的递归定义和暴⼒解法,因此对应的时间复杂度也是指数级的,提交代码以后会TLE。
⼈⼯智能---最清晰的α-β剪枝算法基本思想:根据倒推值的计算⽅法,或中取⼤,与中取⼩,在扩展和计算过程中,能剪掉不必要的分枝,提⾼效率。
定义:α值:有或后继的节点,取当前⼦节点中的最⼤倒推值为其下界,称为α值。
节点倒推值>=α;β值:有与后继的节点,取当前⼦节点中的最⼩倒推值为其上界,称为β值。
节点倒推值<=β;α-β剪枝:(1)β剪枝:节点x的α值不能降低其⽗节点的β值,x以下的分⽀可停⽌搜索,且x的倒推值为α ;(2)α剪枝:节点x的β值不能升⾼其⽗节点的α值,x以下的分⽀可停⽌搜索,且x的倒推值为β ;再上个例题图,⽅便⼤家理解:先做个说明:有画弧线的是与,取较⼩值,没有的是或,去最⼤值。
第⼀步:2、9、3做⽐较,取最⼩值2,I点确定为2第⼆步:J点的1和I点2⼤⼩进⾏⽐较,如果1是J点的最⼩值,由于J的⽗节点是取较⼤值,1<2,⽆法升⾼D的值,所以J点的-1可以点可停⽌搜索,我们划掉该值。
第三步:I点2接着与K点的左值-1进⾏⽐较,如果-1是最⼩值,由于K的⽗节点取较⼤值,-1<2,⽆法升⾼D的取值,所以K点的右值可以停⽌搜索。
第四步:D的值可以确定为2第五步:L点的作⽤值进⾏⽐较,取较⼩值6,D值与L值相⽐较,由于E去较⼤值,假设L就是最⼤值,E=6,⼆B点取得是D和E的较⼩值,2<6,E的结果值⽆法降低B的取值,所以E的右枝可以截掉。
第六步:B的值可以确定为2第七步:N的左右值进⾏⽐较,取0,N点在和O点的左值-5进⾏⽐较,假设-5是最⼩值,0>-5,O点的取值⽆法升⾼⽗节点F的值,所以可以停⽌搜索O点的右枝。
第⼋步:F确定为0.第九步:F点假设是C的最⼩值,它和B点的值⽐较,2>0,也就是说C点的取值⽆法升⾼A点的取值,所以G和H都停⽌搜索。
第⼗步:A点取2.-----------------------------------------------------------------------------------------------【转】。
α-β过程的剪枝规则描述如下:在进行α-β剪枝时,应注意以下几个问题:(1)比较都是在极小节点和极大节点间进行的,极大节点和极大节点的比较,或者极小节点和极小节点间的比较是无意义的。
(2)在比较时注意是与"先辈层"节点比较,不只是与父辈节点比较。
当然,这里的"先辈层"节点,指的是那些已经有了值的节点。
(3)当只有一个节点的"固定"以后,其值才能够向其父节点传递。
(4)α-β剪枝方法搜索得到的最佳走步与极小极大方法得到的结果是一致的,α-β剪枝并没有因为提高效率,而降低得到最佳走步的可能性。
(5)在实际搜索时,并不是先生成指定深度的搜索图,再在搜索图上进行剪枝。
如果这样,就失去了α-β剪枝方法的意义。
在实际程序实现时,首先规定一个搜索深度,然后按照类似于深度优先搜索的方式,生成节点。
在节点的生成过程中,如果在某一个节点处发生了剪枝,则该节点其余未生成的节点就不再生成了。
(1)α剪枝:若任一极小值层节点的β值小于或等于它任一先辈极大值居节点的α值,即α(先辈层)≥β(后继层),则可中止该极小值层中这个MIN节点以下的搜索过程。
这个MIN节点最终的倒推值就确定为这个β值(2)β剪枝:若任一极大值层节点的α值大于或等于它任一先辈极小值层节点的β值,即α(后继层)≥β(先辈层),则可以中止该极大值层中这个MAX节点以下的搜索过程。
这个MAX节点的最终倒推值就确定为这个α值。
通过对图3.10的搜索,来说明α-β剪枝搜索过程。
在搜索过程中,假定节点的生成次序是从上到下,从左到右进行的。
图中带圈的数字,表示节点的计算次序,在叙述时,为了表达上的方便,该序号也同时表示节点。
当一个节点有两个以上的序号时,不同的序号,表示的是同一个节点在不同次序下计算的结果。
过程如下:首先,从根节点开始,向下生成出到达指定节点深度的节点○1{注释:○1应为,○2...○32也一样表示},由○1的值为0,可知○2≤0,继续扩展生成节点○3,由于○3的值5大于○2的值0,并且节点○2再也没有其它的子节点了,所以○4(与○2是同一个节点)的值确定为0。
α-β剪枝算法α-β剪枝算法 前⾯介绍的基本搜索算法,在实际应⽤是是⼗分费时的,因为它需要考虑所有可能的棋步。
有研究表明,在⿊⽩棋的中盘阶段,平均每个局⾯⼤约有10步棋可供选择[1]。
如果程序前瞻10步(搜索深度为10),就需要考虑⼤约100亿个局⾯。
假设计算机以每秒1000万个局⾯的速度进⾏运算,每下⼀步棋⼤约需要运算⼗⼏分钟。
因此,在有限的时间内,程序⽆法进⾏很深的搜索,这就⼤⼤制约了程序的棋⼒。
有没有更⾼效的搜索⽅法呢?Edwards、Timothy(1961年)[2]、Brudno(1963年)[3]等⼈相继在研究中发现,程序搜索过程中有很多局⾯是完全可以忽略的,并提出了α-β剪枝算法(Alpha-beta Pruning)。
我们就仍以图1所⽰的局⾯为例,简要说明剪枝算法的原理。
图1 ⽩先,当前最佳估值为0 假设⽩棋已经搜索了D6的后续变化,得出这步棋的估值为0。
接着开始搜索F4这步棋,⽩棋下F4后形成图2所⽰的局⾯。
图2 ⿊先,当前最佳估值为 6 在这⼀局⾯中,⿊棋相继搜索了C3、D3、E3三步棋,当前最佳估值是E3的 6。
作为⿊棋⽅,他是很乐意看到有E3这样的好棋,他也很希望尚未进⾏搜索的F3和G3能有更好的表现。
但问题是,⿊棋能遇上E3这样好棋的前提是⽩棋要先下出F4,下F4的决定权在于⽩棋⽅。
⽽对于⽩棋⽽⾔,⿊棋的这步E3回应显然对他太不利了。
在他看来,F4这步棋的估值最多只有-6,甚⾄有可能更差。
当⽩棋知道⿊棋将有⼀步好棋E3在等着他时,他是绝对不会去下F4的,因为他完全可以选择下D6,或者等待后续搜索中可能出现的更好棋步。
换句话说,⿊棋根本没有机会⾯对图2所⽰的局⾯,F3和G3这两步棋的结果已经⽆关紧要了,⿊棋没有必要再继续搜索下去。
我们将这种现象称为剪枝(Pruning)。
这看上去是个相当诡异的现象,⿊棋从⾃⼰的利益出发,努⼒寻找尽可能好的棋步,但是⼀旦他的棋步好过头了,对于当前局⾯的搜索⼯作就瞬间变成是多余的。