国际象棋程序设计(四):基本搜索方法

国际象棋程序设计(四):基本搜索方法
 
Franç ois Dominic Laramé e/
 
  这个烦琐、没有代码、像胶水一样的连载,已经进入第四部分了,你们还在看下去吗?如果是的,就写 eMail给我,我就可以找到写这些东西的理由了。   无论如何,这个月的主题是策略类游戏的敌对搜索 (Two-Agent Search) 【译注:意思是站在两个立场上的搜索,我也不知道该用什么比较确切的术语】,要了解它们有什么作用,如何来做,对电脑的程序意味着什么。我们要讨论的技术在所有的游戏中都很重要,但是仅仅靠它们是不足以下象棋的。下个月,我会介绍一些高级的技术,它们能显著增强棋力,同时提高搜索效率。   为什么要搜索?     简单地说,因为我们还没有聪明到可以抛弃搜索的地步。   一个真正聪明的程序可以只依靠棋盘局面来确定,哪一方处于领先以及领先多少,并且制定作战计划让这个优势变为胜果。不幸的是,需要考虑的局面类型太多了,再加上那么多的规则和特例,就是再聪明的程序也不擅长做此类事情。而它们擅长的则是计算。因此对象棋程序来说,与其只根据棋盘的局面来决定好的着法,不如使用它们的蛮力——考虑每一种着法,然后为对手考虑这些着法的所有应对着法,循环下去,直到处理器吃不消为止。   比起掌握复杂的策略,深入的搜索是教电脑下棋的比较简单的方法。例如,考虑马的捉双问题,走一步棋就可以同时攻击两种棋子 (例如一个车和一个后 )。掌握这种类型的走法是需要花一些时间的,更复杂些,我们还要判断这个格子有没有保护。然而,一个愚钝的 3步的搜索就可以学会捉双了,程序最终会尝试让马走到捉双的格子,并探索对手对于捉双的回应,然后吃掉没有逃跑的那个棋子,从而改变了子力平衡。由于全面搜索可以看到每一步,所以不会错过任何机会。如果经过 5步棋后可以产生杀棋或捉死后 (无论怎样不容易看到 ),只要电脑的搜索得足够深,就会看到这个着法。因此,搜索得越深,电脑就会施展越复杂的作战计划。   爷爷时代的“最小-最大”原理     所有双向搜索算法的最基本的思想都是“最小 -最大” (Minimax)原理。它可以追溯到中世纪 (Dark Ages),但我相信它最早是由冯 -诺依曼 (Von Neumann) 【应该是 John von Nuoma 1903-1957 ,美籍匈牙利数学家】60年前完整描述的:      1. 假设有对局面评分的方法,来预测棋手甲 (我们称为最大者 )会赢,或者对手 (最小者 )会赢,或者是和棋。评分用数字表示,正数代表最大者领先,负数代表最小者领先,零代表谁也不占便宜;    2. 最大者的任务是增加棋盘局面的评分 (即尽量让评分最大 )。    3. 最小者的任务是减少棋盘局面的评分 (即尽量让评分最小 )。    4. 假设谁也不会犯错误,即他们都走能让使局面对自己最有利的着法。     那么这该怎么做呢?设想一个简单的游戏,每方只走一步,每步只有两种着法可供选择。评分函数只和最后棋盘的局面有关,即评分是最大者和最小者着法综合的结果。
最大者的着法最小者的着法评分
AC12
AD-2
BC5
BD6
  最大者假设最小者会下出最好的棋,因此他知道,如果他选择着法 A,那么他的对手会回应 D,使最终评分变成 -2(即获胜 )。但是,如果最大者走的着法 B,那么他就会获胜,因为最小者的最佳着法仍然是正数 (5)。所以按照“最小 -最大”算法,最大者会选择着法 B,即使他选择 A并且最小者走错时评分还会更高。   “最小 -最大”原理有个弱点,从简单的例子中还不能明显地看出来——要检查的各种路线的数量是指数形式的,这就意味者工作量会以几何级数增长。      1. 每方有多少种可能的着法,这个数称为分枝因子 (Branch Factor),用 b表示;    2. 考虑的深度用 n表示,通常说“ n层”, n是整数,“层” (Ply)表示一个棋手走的一步棋。例如在上面介绍的迷拟游戏中,搜索深度是 2层。每个棋手走 1步。     例如在象棋中,通常在中局阶段分枝因子大约为 35种着法,在黑白棋中大约为 8。由于“最小 -最大”算法的复杂度是 O(bn),所以对一个局面搜索 4层就需要检查 150万条路线,这是多大的工作量!再增加上去, 5层就会把搜索树膨胀到 5000万, 6层则达到 18亿! 【原作者这里写的是 8 150 万、 9 5000 万、 10 18 亿,不知为何多算了 4 层。】   幸运的是,有办法能在精确度不打折扣的情况下大幅度削减工作量。   Alpha-Beta 搜索 —— 让“最小-最大”法成为现实(也只是有一点点现实)     设想你在迷拟游戏中已经搜索了着法 B,结果你知道最大者在整个游戏中最高得分是 5。   现在假设你开始搜索着法 A了,并且一开始寻找的路线是 A-D,这条线路的得分是 -2。对于最大者来说,这是非常糟糕的,如果他走了 A,那么结果肯定会是 -2,因为最小者总是走得最好的。这是因为,如果 A-CA-D更好,那么最小者会选择 A-D,如果 A-C更坏 (比如说 -20),那么最小者就会选择这条路线。所以,没有必要再去看 A-C以及其他由 A产生的路线了——最大者必须走 B,因为到此位置的搜索已经能证明,无论如何 A是个更糟的选择。   这就是 Alpha-Beta算法的基本思想——只要你有一步好的着法,你就能淘汰其他可能导致灾难的变化,而这样的变化是很多的。如果再跟前面介绍的置换表结合起来,当不同路线的局面发生重复时可以节省下分析局面的时间,那么 Alpha-Beta就能产生无限的能量——在最好的情况下,它处理的结点数是纯粹的“最小 -最大”搜索的平方根的两倍,从 1500万可以减少到 2500  【要说明 Alpha-Beta 搜索的结点数是死办法 ( 即不用 Alpha-Beta 搜索的办法 ) 的平方根的两倍那么多,可以分别计算搜索树中两种类型的结点—— Alpha 结点和 Beta 结点。    Alpha-Beta 搜索是完全搜索,如果某个结点是完全搜索的,那么这个结点称为 Alpha 结点,显然根结点是 Alpha 结点。那么 Alpha 结点的分枝又是什么呢?至少有一个 Alpha 结点,这是肯定的,最好的情况下,剩余的结点都可以产生截断,这些结点称为 Beta 结点。 Beta 结点有个特点,只要它的分枝中有一个 Alpha 结点产生作用,那么剩下的结点就没有搜索的必要了,我们还是取最好的情况,分枝中只有一个 Alpha 结点。   那么如何计算 Alpha 结点的个数呢?一个 Alpha 结点下面有 -  1 Beta 结点,每个 Beta 结点下面又有 1 Alpha 结点,这样深度每增加了两层结点数才扩大 b 倍,因此总的 Alpha 结点数就是 bn/2 。同样道理, Beta 结点也这么计算,得到的结果也是 bn/2 ,因此总结点数就是 2bn/2 。】   对着法排序来优化 Alpha-Beta 搜索     可是,我们如何才能达到预期的效果呢?我们是否还需要做其他事情?   的确是的。只要 Alpha-Beta搜索可以找到比其他着法好的着法,它就能对搜索树作出非常有效的裁减,这就意味着,关键在于首先搜索好的着法。当我们在搜索其他着法以前先搜索到最好的着法,那么最好的情况就发生了。然而最坏的情况,搜索着法的顺序是按评分递增的,即每次搜索到的着法都比曾经搜索的着法要好,那么这种情况下的 Alpha-Beta搜索就无法作出任何裁减,这种搜索将退化为极其浪费的“最小 -最大”搜索。 【这就是前一章的标题中写道“也只是有一点点现实”的原因。】   对搜索进行排序是相当重要的。让着法随便排列肯定不行,我们必须找到更聪明的办法。不幸的是,如果有简单的办法知道最好的着法,那还有搜索的必要吗?因此我们必须用“猜最好的着法”来对付。   有很多技术可以让着法的顺序排列成尽可能好的顺序:      1. 用评估函数对着法打分,然后排序。直觉上这会起到作用,评估函数越好,这个方法就越有效。不幸的是在象棋中它一点也不起作用,因为下个月我们将了解到,很多局面是不能准确评估的。    2. 找到在置换表中已经存在的局面,如果它的数值足够好,就会产生截断,这样就不必再进行其他搜索了。    3. 尝试特定类型的着法。例如,后被吃掉肯定是最坏的想法,所以先检查吃子的着法是行之有效的。    4. 把这个思路进行拓展,关注已经在同一层深度产生过截断的着法。“杀手启发” (Killer Heuristic)是建立在很多着法是次序无关的基础上的。如果你的后被攻击了,不管你把 H2兵挺一格还是两格,对手都会吃掉你的后。因此,如果在搜索 H2-H3着法时,“象吃掉后”的着法会产生截断,那么在搜索 H2-H4着法时,“象吃掉后”很有可能也产生截断,当然应该首先考虑“象吃掉后”这一步。    5. 再把杀手启发拓展到历史表上。如果在前面几回合的搜索中,发现 G2-E4的着法很有效,那么很有可能现在仍然很有用 (即便原来的格子是象而现在变成了后 ),因为棋盘其他位置的情况不太可能有多少变化。历史启发的的实现非常简单,只需要一个 64x64的整数数组,它可以起到很可观的效果。   现在已经谈了所有宝贵的思想,然而最有效的方法却稍稍有背于人的直觉,这个方法称为“迭代加深” (Iterative Deepening)。   迭代加深的 Alpha-Beta 搜索     如果你正在搜索 6层的局面,那么理想的着法顺序应该由同样层数搜索的结果来产生。既然这明显是不可能做到的,那么能否用稍浅的搜索 (比如说 5)来代替呢?   这就是迭代加深方法背后的思想——一开始搜索 2层,用这样种搜索产生的着法顺序来搜索 3层,反复如此,直到到达规定的深度。   这个技术会受到一般人的质疑——大量的努力是重复的 (810次乃至更多 ),不是吗?   那么考虑一下搜索树的深度 n和分枝因子 b好了。搜索树在第 1层的结点数为 b,第 2层是 b2,第三层是 b3,等等。如果 b很大 (记住中局时在 35左右 ),那么绝大多数工作量取决于最后一层。重复搜索 (n - 1)的深度其实是小事一桩,即便迭代加深一点也不起作用,它也只会多花 4%的时间 (在通常的局面上 )。   但是,它的优势是非常可观的——由浅一层搜索的结果排列得到的顺序,在层数加深时可以大幅度增加截断率。因此,迭代加深的 Alpha-Beta搜索实际上找的结点数,会比直接的 Alpha-Beta搜索的少很多。在使用置换表后,它的收效就更可观了——重复搜索浅的部分就几乎不需要时间了,因为这些结果在置换表里都有,没有必要重新计算了。   【需要指出的是,实际运用中通常只对根结点进行迭代加深,这样搜索的结点数是 1 + b +  …  bn ,比 bn 大不了多少。如果每个结点都用迭代加深,则需要搜索的结点数就是 (1 + b)n ,刚才提到在最好的情况下分枝因子为合理着法数的平方根,即 b 6 左右,而 6n 7n 是有很大区别的。   事实上,要在每个结点上都使用迭代加深,只要检查置换表就可以了,因为从根结点处做深一层的搜索时,除了新增加的一层结点以外,其他各层结点上都有最好的着法存储在置换表中了,这些着法应该优先考虑,绝大多数着法能产生裁剪,而不需要检查杀手表或者做产生产生。   另外,迭代加深可以大幅度提高历史表的效率,在前一次 N 层的搜索中历史表已经有相当丰富的历史着法信息了,在做 N + 1 层搜索时,这些历史信息可以让着法有更好的顺序。】   电脑下棋的风格     迭代加深的 Alpha-Beta搜索和置换表 (并且在历史表的推动下 )能让计算机对局面搜索到相当的深度,并且可以下国际象棋了。应该这么说,它的祖先“最小 -最大”算法决定了那台电脑 (曾经在世界上有惊人之举的那台电脑 【即冯 - 诺依曼的 ENIAC )下棋的风格。   例如,设想电脑能对一个局面搜索 8层,通常在考虑某一步时,这种让人不解的模式会让让它战胜对手。只有少数一些看不清的、复杂的、迷惑人、超出直觉局面,才能让对手抓住一线机会取得胜利,而对于人类棋手 (甚至大师 )来说,这样的局面陷阱已经可以写到书里去了。   然而,如果电脑找到了一个导致和棋的无聊着法,它就会绕过陷阱,因为它假设对手会作出正确的应对,无论那种可能性有多小,只要电脑认为平局是最高的期望。   结果你可能会说,电脑下棋会极端谨慎,就像对手都是世界冠军一样。如果考虑到电脑算不到出人类棋手布置的陷阱那个深度,人类就会绞尽脑汁布设陷阱,让电脑犯错误。 (人类棋手会研究对手的下棋风格,如果卡斯帕罗夫有机会和深蓝下一百盘棋,他可能会找到深蓝的弱点并且打败它。但是我们不知道有没有这种可能性。 【现在看来,卡斯帕罗夫战胜深蓝是不在话下的,因为近几年他一直在和水平更高的电脑较量。】 )   下一个月     在第五部分,我们会讨论直接或改变深度的 Alpha-Beta搜索的局限。我们还会讨论如何利用一些技术,诸如空着启发 (Null-Move Heuristic)、静态搜索 (Quiescence Search)、期望搜索 (Aspiration Search)MTD(f)搜索,以及让深蓝名声显赫的“单步延伸” (Singular Extensions)技术,它们会提高下棋水平。坚持住,我们快要结束了!      Franç ois Dominic Laramé e20008月     原文: http://www.gamedev.net/reference/programming/features/chess4/   译者:象棋百科全书网  ( webmaster@xqbase.com )   类型:全译加译注
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值