[搜索算法] Alpha-Beta算法

目录

  • 01 Alpha-Beta算法的产生
  • 02 Alpha-Beta算法的剪枝原理
  • 03 Alpha-Beta算法的剪枝实现
  • 04 Alpha-Beta算法的二次优化

内容

01 Alpha-Beta算法的产生

Alpha-Beta算法的原生是极大极小算法。在极大极小算法中,即使我们已经稍微优化了一下代码,使得极大极小算法进化成负值最大算法。但是仔细回想,负值最大算法并没有对算法的实质产生任何优化,只是对于代码设计的优化。同时也在谈论深度的时候说明了,其实极大极小算法实际上是以树为抽象结构进行的。仔细研究发现,我们可以通过树的Alpha-Beta剪枝来优化算法。

我们可以尝试继续模拟一下极大极小的例子:若深度为2,我方白棋,面对黑方已经下过的局面,寻找最佳落子点

02 Alpha-Beta算法的剪枝原理

​我以树为递归遍历的抽象数据类型,介绍遍历的过程:

  1. 模拟我(白)方下棋;
  2. 深度为2,递归程序;
  3. 模拟黑方下棋;
  4. 深度为1,递归程序;
  5. 模拟白方下棋;
  6. 深度为0,评估;
  7. 返回评估最大;
  8. 返回评估最小;
  9. ……

为了避免读者看图混淆,这里提醒一下:

​接下来将举例极大极小算法的明显冗余现象,同时笔者将在图中注释,方便阅读。

极大值冗余

极小值冗余

在这里插入图片描述

03 Alpha-Beta算法的剪枝实现

​我们已经知道,在极大极小算法中会存在最大最小值冗余,则Alpha-Beta剪枝的目的就在于去除掉那些徒劳的工作。我们可以得知,发生徒劳的具体的条件是:

  1. 不是迭代的头遍历,由上面两个例子来看,必须在该迭代之前就已经出现一个的值。那个值将决定之后的遍历是否舍弃。(极大值冗余中的白棋值6,极小值冗余的黑棋值4)为了方便书写,笔者暂时将这种值称为已知最大(小)值。

  2. 需要一个大小判断:

    a. 若对于当前遍历是黑棋:那么判断 上层白棋中的已知最大值 是否大于等于 当前(黑棋)最小值。若成立则结束当前遍历,若不成立则继续遍历。

    b. 若对于当前遍历是白棋:那么判断 上层黑棋中的已知最小值 是否小于等于 当前(白棋)最大值。若成立则结束当前遍历,若不成立则继续遍历。

  3. 冗余的情况仅发生在相邻两层的子递归中。

​那么如何实现Alpha-Beta剪枝的呢?我们可以稍微推理一下:

  1. 传递一个已知大值和已知最小值时,只能通过递归传值。

  2. 其实对于上面条件的1,迭代的头遍历,我们可以将已知最大值设置为系统最小值,将已知最小值设置为系统最大值,这样就不需要将头遍历设置为一个额外的情况。然后在头遍历的时候将已知最大(小)值代替即可。

  3. 整个过程时基于极大极小算法的雏形推理的。

​那么我们可以写出分离的Max和Min函数:

int MinMax(int depth, int KnownMax, int KnownMin) {
	if (SideToMove() == Myself) return Max(depth, KnownMin);
	else return Min(depth, KnownMax);
}
int Max(int depth, int KnownMin) {
	int best = -INFINITY;
	if (depth <= 0) return Evaluate();
	GenerateLegalMoves();
	while (hasLegalNext()) {
		MakeNextMove();
		val = Min(depth - 1, best);//将 当前最大值 传递成为 子递归的已知最大值
		UnmakeMove();
		if (val > best) best = val;//得到当前最大值
        if (best >= KnownMin) return KnownMin;//与已知最大值比较,若已知最小值 小于等于 当前最大值,则返回已知最小值。
	}
	return best;
}
int Min(int depth, int KnownMax) {
	int best = INFINITY;
	if (depth <= 0) return Evaluate();
	GenerateLegalMoves();
	while (hasLegalNext()) {
		MakeNextMove();
		val = Max(depth - 1, best);//将 当前最小值 传递成为 子递归的已知最小值
		UnmakeMove();
		if (val < best) best = val;//得到当前最小值 
       	if (best <= KnownMax) return KnownMax;//与已知最大值比较,若已知最大值 大于等于 当前最小值,则返回已知最大值        
	}
	return best;
}

04 Alpha-Beta算法的二次优化

​对于上面的结果,Alpha-Beta最终算法进行二次优化,二次优化有两个地方:

  1. 对于一些细节优化;
  2. 对于代码的设计优化;
细节优化

​因为只是代码的细节优化,Max和Min都几乎相同,我这里以Max举例:

单独提出可以优化的代码块:

val = Min(...);
if (val > best) best = val;
if (best >= KnownMin) return KnownMin;

那么我们可以知道,若递归能够进行,那么best一定是小于KnownMin的,而val替代best的方法则是若大于best,那么其实类似于:

-------best-------val--------KnownMin-------->x轴//若能够继续递归,best、val、KnownMin的大小情况

那么代码可以优化成:

val = Min(...);
if (val >= KnownMin) return KnownMin;
if (val > best) best = val;

这样优化减少了val和best的逻辑判断,浅浅优化了时间。在任何的算法中,我们要秉持能优化的地方绝对不放弃的态度。

​那么优化后的Max代码为:

int Max(int depth, int KnownMin) {
	int best = -INFINITY;
	if (depth <= 0) return Evaluate();
	GenerateLegalMoves();
	while (hasLegalNext()) {
		MakeNextMove();
		val = Min(depth - 1, best);//将 当前最大值 传递成为 子递归的已知最大值
		UnmakeMove();
		if (val >= KnownMin) return KnownMin;//当 当前值 大于等于 已知最小值,则返回已知最小值
		if (val > best) best = val;//
	}
	return best;
}

​接下的细节优化需要图示举例:

​那么有价值的低位白棋的情况为:

​那么可得,若当前的白棋有价值,则:

------KnownMax------val--------KnownMin---------->

​那么我们需要在递归的时候将已知最大值传递下去,则算法变为:

int MinMax(int depth, int KnownMax, int KnownMin) {
	if (SideToMove() == Myself) return Max(depth, KnownMax, KnownMin);
	else return Min(depth, KnownMax, KnowMin);
}
int Max(int depth, int KnownMax, int KnownMin) {
	if (depth <= 0) return Evaluate();
	GenerateLegalMoves();
	while (hasLegalNext()) {
		MakeNextMove()
		val = Min(depth - 1, best, KnownMin);//将 当前最大值 传递成为 子递归的已知最大值
		UnmakeMove();
		if (val >= KnownMin) return KnownMin;//当 当前值 大于等于 已知最小值,则返回已知最小值
		if (val > KnownMax) KnowMax = val;
	}
	return best;
}

int Min(int depth, int KnownMax, int KnownMin) {
	if (depth <= 0) return Evaluate();
	GenerateLegalMoves();
	while (hasLegalNext()) {
		MakeNextMove();
		val = Max(depth - 1, KnownMax, best);//将 当前最小值 传递成为 子递归的已知最小值
		UnmakeMove();
		if (val <= KnownMax) return KnownMax;//当 当前值 大于等于 已知最小值,则返回已知最小值
		if (val < KnowMin) KnownMin = val;  
	}
	return best;
}

​最终算法实现了剪枝、减少变量、去掉额外的运算。

设计优化

​在极大极小值算法中提到,我们可以将最终的算法变为负值极大值算法。同理,我们可以将上述的Alpha-Beta进化成为结构更加简单的算法。原理几乎相同,相反数交替双方的角色即可。同时将高层已知最大值(knownMax)变成Alpha,上层已知最小值(knowMin)变成Beta。下面是逻辑代码:

int AlphaBeta(int depth, int alpha, int beta) {
	if (depth == 0) return Evaluate();
	GenerateLegalMoves();
	while (hasLegalNext()) {
		MakeNextMove();
		val = -AlphaBeta(depth - 1, -beta, -alpha);
		UnmakeMove();
		if (val >= beta) return beta;//第一次优化效果
		if (val > alpha) alpha = val;//第二次优化效果,alpha经过两层反转成为原来的值。
	}
	return alpha;
}
  • 24
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
======================================================================== MICROSOFT FOUNDATION CLASS LIBRARY : fir ======================================================================== AppWizard has created this fir application for you. This application not only demonstrates the basics of using the Microsoft Foundation classes but is also a starting point for writing your application. This file contains a summary of what you will find in each of the files that make up your fir application. fir.dsp This file (the project file) contains information at the project level and is used to build a single project or subproject. Other users can share the project (.dsp) file, but they should export the makefiles locally. fir.h This is the main header file for the application. It includes other project specific headers (including Resource.h) and declares the CFirApp application class. fir.cpp This is the main application source file that contains the application class CFirApp. fir.rc This is a listing of all of the Microsoft Windows resources that the program uses. It includes the icons, bitmaps, and cursors that are stored in the RES subdirectory. This file can be directly edited in Microsoft Visual C++. fir.clw This file contains information used by ClassWizard to edit existing classes or add new classes. ClassWizard also uses this file to store information needed to create and edit message maps and dialog data maps and to create prototype member functions. res\fir.ico This is an icon file, which is used as the application's icon. This icon is included by the main resource file fir.rc. res\fir.rc2 This file contains resources that are not edited by Microsoft Visual C++. You should place all resources not editable by the resource editor in this file. ///////////////////////////////////////////////////////////////////////////// For the main frame window: MainFrm.h, MainFrm.cpp These files contain the frame class CMainFrame, which is derived from CFrameWnd and controls all SDI frame features. ///////////////////////////////////////////////////////////////////////////// AppWizard creates one document type and one view: firDoc.h, firDoc.cpp - the document These files contain your CFirDoc class. Edit these files to add your special document data and to implement file saving and loading (via CFirDoc::Serialize). firView.h, firView.cpp - the view of the document These files contain your CFirView class. CFirView objects are used to view CFirDoc objects. ///////////////////////////////////////////////////////////////////////////// Other standard files: StdAfx.h, StdAfx.cpp These files are used to build a precompiled header (PCH) file named fir.pch and a precompiled types file named StdAfx.obj. Resource.h This is the standard header file, which defines new resource IDs. Microsoft Visual C++ reads and updates this file. ///////////////////////////////////////////////////////////////////////////// Other notes: AppWizard uses "TODO:" to indicate parts of the source code you should add to or customize. If your application uses MFC in a shared DLL, and your application is in a language other than the operating system's current language, you will need to copy the corresponding localized resources MFC42XXX.DLL from the Microsoft Visual C++ CD-ROM onto the system or system32 directory, and rename it to be MFCLOC.DLL. ("XXX" stands for the language abbreviation. For example, MFC42DEU.DLL contains resources translated to German.) If you don't do this, some of the UI elements of your application will remain in the language of the operating system. /////////////////////////////////////////////////////////////////////////////

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值