📄 高级搜索方法——空着裁剪.htm
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<!-- saved from url=(0058)http://www.elephantbase.net/computer/advanced_nullmove.htm -->
<HTML><HEAD><TITLE>高级搜索方法——空着裁剪</TITLE>
<META http-equiv=Content-Type content="text/html; charset=gb_2312-80">
<META content="MSHTML 6.00.3790.536" name=GENERATOR></HEAD>
<BODY background=高级搜索方法——空着裁剪_files/background.gif>
<DL dir=ltr>
<DIV align=center>
<CENTER>
<DT><FONT size=3>《对弈程序基本技术》专题</FONT> </CENTER></DT></DIV>
<DIV align=center>
<CENTER>
<DT> </CENTER></DT></DIV>
<DIV align=center>
<CENTER>
<DT><FONT face=隶书 size=6>空着向前裁剪</FONT> </CENTER></DT></DIV>
<DIV align=center>
<CENTER>
<DT> </CENTER></DT></DIV>
<DIV align=center>
<CENTER>
<DT><FONT face="Times New Roman">Bruce Moreland (</FONT><A
href="mailto:brucemo@seanet.com"><FONT
face="Times New Roman">brucemo@seanet.com</FONT></A><FONT
face="Times New Roman">) / </FONT>文 </CENTER></DT></DIV>
<DIV align=center>
<CENTER>
<DT> </CENTER></DT></DIV>
<DT><FONT face=楷体_GB2312 size=5><STRONG>没有副作用即可获得额外的速度</STRONG></FONT>
<DT>
<DT> 空着向前裁剪<FONT face="Times New Roman">(Null-Move Forward
Pruning)</FONT>,运用可能忽视重要路线的冒险策略,使得国际象棋的分枝因子锐减,它导致搜索深度的显著提高,因为大多数情况下它明显降低了搜索的数量。它的工作原理是裁剪大量无用着法而只保留好的。
<DT> 这个技术在很多刊物上报道过,但是使得大家都来关注空着的,则是由<FONT face="Times New Roman">Chrilly
Donniger</FONT>发表在<FONT face="Times New Roman">1993</FONT>年<FONT
face="Times New Roman">9</FONT>月的<FONT
face="Times New Roman">ICCA</FONT>杂志上的一篇文章。
<DT> 试想国际象棋搜索树中的某个局面,你的程序将以<FONT
face="Times New Roman"><EM>D</EM></FONT>层搜索这个局面的每个着法。如果其中任何一个着法的分数超过<FONT
face="Times New Roman">Beta</FONT>,你就会马上返回<FONT
face="Times New Roman">Beta</FONT>。如果任何一个超过<FONT
face="Times New Roman">Alpha</FONT>,但是没有超过<FONT
face="Times New Roman">Beta</FONT>,你就要记住着法和分值,因为这有可能是主要变例的一部分。如果它们全部小于或等于<FONT
face="Times New Roman">Alpha</FONT>,你就要返回<FONT
face="Times New Roman">Alpha</FONT>。
<DT> 空着向前裁剪是你搜索任何着法之前要做的事。你要问一个问题:“如果我在这里什么都不做,对手能做什么?”
<DT> 记得在刚才,你没有问这个问题。你只是去找最佳的着法去打击对手。问对手是否会打击你,这个问题却有所不同。
<DT> 但是事实证明很多情况下对手无法打击你。比如说你送了一个车,而其他棋子都没有作用,在这种情况下,对手随便走哪步你都吃亏,因为你丢了一个车。空着向前裁剪的要点,就是简单地去掉那些没用的着法,而不要在这上面多花时间。
<DT> 这就好比像打架时,根据自己的能力给对手一个出击的机会,来增加自己的信心。如果任凭对手攻击也无法击倒你,那么你攻击他的时候他会输掉。
<DT> 我们不讨论这个策略了,现在来谈它是如何运用在电脑国际象棋中的。在你搜索着法以前<FONT
face="Times New Roman">(</FONT>事实上在你生成着法以前<FONT
face="Times New Roman">)</FONT>,你做一个减少深度的搜索,让对手先走,如果这个搜索的结果大于或等于<FONT
face="Times New Roman">Beta</FONT>,那么你简单地返回<FONT
face="Times New Roman">Beta</FONT>而不需要搜索任何着法。
<DT> 这个思想就给了对手出击的机会,如果你的局面仍然好到超过<FONT
face="Times New Roman">Beta</FONT>的程度,你就假设如果你搜索了所有的着法也会超过<FONT
face="Times New Roman">Beta</FONT>。
<DT> 这个方法能节省时间的原因是,开始时用了减少深度的搜索。深度减少因子称为<FONT
face="Times New Roman"><EM>R</EM></FONT>,因此跟你用深度<FONT
face="Times New Roman"><EM>D</EM></FONT>搜索所有的着法相比,现在你是先以<FONT
face="Times New Roman"><EM>D</EM> </FONT><FONT face=Symbol>-</FONT><FONT
face="Times New Roman"> <EM>R</EM></FONT>搜索对手的着法。一个比较好<FONT
face="Times New Roman"><EM>R</EM></FONT>是<FONT
face="Times New Roman">2</FONT>,如果你要对所有的着法搜索<FONT
face="Times New Roman">6</FONT>层,你最终只对对手所有的着法搜索了<FONT
face="Times New Roman">4</FONT>层。<FONT
color=#0000ff>【译注:因为放弃着法后层数应该减1,所以实际在对手上面搜索的层数是</FONT><FONT
face="Times New Roman" color=#0000ff><EM>D</EM> </FONT><FONT face=Symbol
color=#0000ff>-</FONT><FONT face="Times New Roman" color=#0000ff> 1
</FONT><FONT face=Symbol color=#0000ff>-</FONT><FONT face="Times New Roman"
color=#0000ff> <EM>R</EM></FONT><FONT color=#0000ff>。】</FONT>
<DT> 这就使得很多时间节约下来了,实践证明可以使搜索增加一到两层。效果真的非常可观!
<DT> 代码如下,醒目的文字是在<FONT face="Times New Roman">Alpha-Beta</FONT>搜索的基础上增加的部分:
<DD>
<DD><FONT face=宋体 color=#ff0000>#define R 2</FONT>
<DD><FONT face=宋体>int AlphaBeta(int depth, int alpha, int beta) {</FONT>
<DD><FONT face=宋体> if (depth == 0) {</FONT>
<DD><FONT face=宋体> return Evaluate();</FONT>
<DD> }
<DD><FONT face=宋体 color=#ff0000> MakeNullMove();</FONT>
<DD><FONT face=宋体 color=#ff0000> val = -AlphaBeta(depth - 1 - R, -beta, -beta
+ 1);</FONT>
<DD><FONT face=宋体 color=#ff0000> UnmakeNullMove();</FONT>
<DD><FONT face=宋体 color=#ff0000> if (val >= beta) {</FONT>
<DD><FONT face=宋体 color=#ff0000> return beta;</FONT>
<DD><FONT color=#ff0000> }</FONT>
<DD><FONT face=宋体> GenerateLegalMoves();</FONT>
<DD><FONT face=宋体> while (MovesLeft()) {</FONT>
<DD><FONT face=宋体> MakeNextMove();</FONT>
<DD><FONT face=宋体> val = -AlphaBeta(depth - 1, -beta, -alpha);</FONT>
<DD><FONT face=宋体> UnmakeMove();</FONT>
<DD><FONT face=宋体> if (val >= beta) { //
</FONT>把这部分去掉,就用纯粹的最小-最大代替了Alpha-Beta。
<DD><FONT face=宋体> return beta;</FONT>
<DD><FONT face=宋体> </FONT>}
<DD><FONT face=宋体> if (val > alpha) {</FONT>
<DD><FONT face=宋体> alpha = val;</FONT>
<DD><FONT face=宋体> }</FONT>
<DD><FONT face=宋体> </FONT>}
<DD><FONT face=宋体> return alpha;</FONT>
<DD><FONT face=宋体>}</FONT>
<DT>
<DT> 在这个代码中我用了一个诀窍。我需要知道空着搜索的值是否是<FONT
face="Times New Roman">Beta</FONT>或更好,如果还不如<FONT
face="Times New Roman">Beta</FONT>,我不关心它到底比<FONT
face="Times New Roman">Beta</FONT>有多糟,因此我用了极小窗口,试图让裁剪做得更快。实际上我用<FONT
face="Times New Roman">(Beta </FONT><FONT face=Symbol>-</FONT><FONT
face="Times New Roman"> 1, Beta)</FONT>调用了搜索,但是由于递归时必须把<FONT
face="Times New Roman">Alpha</FONT>和<FONT
face="Times New Roman">Beta</FONT>颠倒并取负数,这就变成源代码中的样子了。
<DT> 不用说,这个代码在一方被将军时不能发挥作用<FONT
face="Times New Roman">(</FONT>因为对手立刻把王吃掉了<FONT
face="Times New Roman">)</FONT>。什么地方允许调用空着向前裁剪,必须掌握好分寸,因为如果你允许一次连续地这么做,那么搜索将退化成什么都不做了。一个很简单的尝试,就是当中没有间隔一个实在着法的时候,不要让两个空着搜索连在一起。另一个思想是在一个实在着法之前,允许连续两个空着裁剪。实践证明这两个方法都做得很好。
<DT>
<DT><FONT face=楷体_GB2312 size=5><STRONG>嗯,还是有副作用的</STRONG></FONT>
<DT>
<DT> 不幸的是,空着向前裁剪在某些地方不能正常运行。我们作了一个重要的假定——假定走了一步棋会比不走棋有更高的分值。不幸的是,这个假定在很多典型的局面上并不成立,这些局面非常普遍,并且有一个名称——无等着局面<FONT
face="Times New Roman">(Zugzwang)</FONT>。无等着局面指的是,如果你不走棋,局势会好些,但是你被强迫走子,这使得你的局势会崩溃。下面是个典型的例子:
<DT>
<DIV align=center>
<CENTER></DIV>
<DT><IMG height=256 src="高级搜索方法——空着裁剪_files/advanced_nullmove1.gif" width=256>
</CENTER>
<DIV></DIV>
<DT>
<DT> 在这个局面里,如果白方被迫走子,他走 <FONT face="Times New Roman">Kb2</FONT>,而黑走 <FONT
face="Times New Roman">Kd2 </FONT>助兵变后。如果白方不走棋,那么黑的走 <FONT
face="Times New Roman">Kc3</FONT>,局面就是和棋。<FONT
face="Times New Roman">(</FONT>事实上这是一个互相的无等着局面,因为双方都被先走棋所困扰,这个问题不在我们的讨论范围内。<FONT
face="Times New Roman">)</FONT>
<DT> 如果到达这个局面,而且试图用空着向前裁剪,那么可能会认为黑方没有威胁白方,因为现在黑方确实没威胁白方。而现在黑方在等待白方毁掉局势,这就完全不同了。
<DT> 由于这个原因,空着向前裁剪不能在残局中使用,或者至少不能直接地使用。如果你试图在残局中用,则会出现很糟糕的事情。
<DT> 有一个更困扰人的例子,是这样的:
<DIV align=left></DIV>
<DT>
<DIV></DIV>
<DIV align=center>
<CENTER></DIV>
<DT><IMG height=256 src="高级搜索方法——空着裁剪_files/advanced_nullmove2.gif" width=256>
</CENTER>
<DIV></DIV>
<DT>
<DT> 这个局面选自<FONT face="Times New Roman">Goetsch</FONT>和<FONT
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -