博弈树与α-β剪枝

一、评价函数(Evaluation function)

绝大部分的游戏,决策空间都相当庞大。

博弈树与α-β剪枝_第1张图片

即使是最简单的三子棋(又叫做“井”字棋,一字棋)。它的第一步有9种决策,然后对面有9*8=72种决策,....,最后一层的决策个数达到了 9! = 362880 种。如此简单的游戏,在不做特殊处理的时候,都有几十万种决策(当然这个量级计算机还是能够hold住的)。它的棋盘大小仅仅是3 X 3,五子棋是15 X 15,围棋是19 X 19,想要穷举出所有决策,几乎是不可能的。

因此,我们不能够像上一章那样,每次都穷举出所有的结果,再去慢慢找最优决策。随着树的深度的增加,我们的节点个数是指数级上升的。

我们不得不搜索到一定程度,就停止继续往下搜索。

当我们停下来以后,这个时候,由于我们游戏还没有结束,我们如何判断当前的结果的好坏?

我们需要设计一个评价函数(Evaluation function)对于当前局面进行评分。这个评价函数如何设计?主要是根据不同的游戏,还有人类的日常经验来判断。

我当时设计五子棋AI的时候,就人为的设计了一个评价当前局面的分数的函数。比如已经有5个子连成一线了,它就是最高分;如果有4个子连成一线,它就是次高分;还有双3,...。这样我们就能根据局面,获得一个得分。当然,当对面调用这个评价函数的时候,获得的分数前面要取一个负号。因为对手的最高分,就是我们的最低分。

二、博弈树 与 α-β剪枝

有了评价函数,我们就可以随时终止我们的搜索了。因为对于任何局面,我们都能够给出一个收益得分 。我们可以限定我们的搜索的深度,随时结束搜索。

但是我们的搜索空间仍然非常庞大。因为最开始的几层,可做的决策是相当多的。

比如五子棋,第一步就有225种下法。而对手对应就有225*224=50,400种决策;再往下一层,就有225*224*223=11,239,200种。这才第三层,就已经快爆炸了。

一般五子棋的高手都能想到后面五六步,甚至十几步。想要与之对抗,我们必须得想办法减少我们的搜索数量,增加我们的搜索深度,这样我们的AI才能看得更远的未来,想得更多,这样棋力才会变强。

这里,我们用到了强大的α-β剪枝技术。它的思路就是,减少所有没有必要的搜索,及时终止,从而节省算力,同时又不能漏过所有可能的最优解。下面来详细介绍一下。

我们先来理解一下,怎么样的搜索是没有必要的,假设我们限定了搜索深度为3,我们从头开始搜索,如下:

博弈树与α-β剪枝_第2张图片

我们从根节点往下搜,直到第一个叶子节点:

博弈树与α-β剪枝_第3张图片

此时,到达了第一个深度为3的节点,此时我们调用估值函数,假设我们获得它的收益为3,现在我们回头来看它的父节点:

博弈树与α-β剪枝_第4张图片

由于,这个父亲节点是MIN节点,我们知道,它总是会选择子节点中最小值。现在,子节点已经出现了一个值为3。

现在仔细想想,如果我们继续获得它的子节点的收益,为一个比3要大的值,假设为12好了。那么当前的父节点,必然不会选择这个12,而会去选择3。因此,这个父亲节点的收益,无论如何,都不会超过3,那么它的取值范围,我们可以认为是:(-∞,3]。也就是说,我们的子节点,其实更新了它的父节点的收益的一个上界值,如图:

博弈树与α-β剪枝_第5张图片

到这里,我们其实并没有进行剪枝,只是找到了一个父节点的上界值(β值),我们还是得继续搜索它的子节点:

博弈树与α-β剪枝_第6张图片

假如我们搜索到了12,我们依然试图更新父节点的上界值(β值),但是因为比3要大,更新失败了,继续搜索下一个,直到搜索完所有的当然父节点的所有子节点:

博弈树与α-β剪枝_第7张图片

当它所有的子节点都被搜索完以后,我们其实可以知道当前节点的收益就是3了。这个时候我们可以修改它的下界为3,收益为3。

注意这个时候,其实跟之前的极大极小树的搜索过程没有区别,我们并没有进行任何的剪枝。接下来继续搜索:

 

博弈树与α-β剪枝_第8张图片

我们确定了当前节点收益为3,再去看它的父节点,即根节点。根节点原本的收益值范围是(-∞,+∞)。现在我们找到了一个子节点收益是3。

根节点是一个MAX节点,跟之前相反,子节点的收益值3,可以用来更新的是根节点的下界(α值)。至于为什么,可以类比一下之前的。我们现在已经有搜索到一个3,如果我们以后搜索到比3小的值,那么根节点在取最大值的时候,肯定会选择更大的3,而不是其他值。因此最优解的下界就是3,不会再更小了。

我们带着根节点的取值区间[3, +∞)继续往下搜索,把这个区间赋给下一个子节点:

博弈树与α-β剪枝_第9张图片

往下继续深度优先遍历,访问它的第一个子节点。此时到达设定的深度3,我们调用评价函数,假设评价函数返回值为2:

博弈树与α-β剪枝_第10张图片

注意,重点来了。

我们知道,当前子节点是一个收益为2的MAX节点。MAX节点可以更新父节点的上界。因此,父亲节点的上界,被修改成了2。 这里就出现了一个矛盾的区间[3,2],如下图:

博弈树与α-β剪枝_第11张图片

观察当前的节点,它的收益值的取值区间是[3,2]。这明显是不合理的,收益不可能下界是3,同时上界又是2。我们可以做出判断,这个节点无论如何都不可能是最优解。

由于这个区间已经产生了矛盾,我们可以直接给当前节点判死刑,跳过剩下所有的子节点了:

博弈树与α-β剪枝_第12张图片

上面的操作叫做α-β剪枝(到底是α还是β我也搞不清)。

你可以仔细想一想。总之,收益值的可行区间一旦变成矛盾的,说明当前节点必然不会是获得最优的决策,那么我们可以直接跳过这个节点,不管它还有多少个子节点没有被搜索。

---------------------- 踏实的分割线 ----------------------

Tips: 如果你觉得我上面一段很有道理,可以忽略这部分内容。

如果你是一个严谨的怀疑论者,心里不踏实,请继续看下去下面的证明过程。

我们就来仔细分析讨论一下,假如我们接着往下搜索会发生什么:

博弈树与α-β剪枝_第13张图片

 

剩下的值无非就两种情况,我们全都来讨论一遍:

第一种,搜索到比2小的值,比如1:

博弈树与α-β剪枝_第14张图片

那么它的父亲节点是个MIN节点,会选择比2更小的1,假设最后中间的节点收益就是这个1,再看根节点:

博弈树与α-β剪枝_第15张图片

由于根节点是MAX节点,因此,即使找到了收益更小的1,根节点并不会选择它,而是选择更大的值为3的左边那个节点。

第二种情况,搜索到比2大的值,比如5:

博弈树与α-β剪枝_第16张图片

它并不能代替2,所以中间节点的收益还是2。在根MAX节点做取舍的时候,依然还是会选择更大的左边的那个3的节点。

因此,我们可以放心地说,当发现收益值的区间产生矛盾的时候,我们当前的节点无论再怎么继续搜索,也不可能出现最优解了。这下可以放心跳过了。

---------------------- 踏实的分割线 ----------------------

在这个例子里面,我们已经给中间那个点判了死刑,直接跳过它剩下的2个子节点,转到了右边那个节点。

博弈树与α-β剪枝_第17张图片

老规矩,把父亲节点的可行区间传递给当前子节点,继续往下深度优先遍历:

博弈树与α-β剪枝_第18张图片

到了叶子节点,调用估值函数,假设这回返回的收益是14:

博弈树与α-β剪枝_第19张图片

由于14是MAX层,试图更新父节点的上界。没有产生矛盾区间,继续往下搜。假设下一个搜索到的是1:

博弈树与α-β剪枝_第20张图片

更新上界,产生矛盾区间,停止继续搜索。到这里我们遍历了根节点的所有可能子节点,可以做出最终判断,根节点的收益最终值为3,以及得到最优的路径:

博弈树与α-β剪枝_第21张图片

通过维护一个收益的可行区间,在搜索的过程中进行剪枝操作,就是所谓的α-β剪枝

由于α-β剪枝剪掉的点,都是必然不可能是最优解的节点,因此我们永远不会错过最优解。同时,由于及时的剪枝操作,我们大大地减少了需要搜索的节点数量,节省下来的算力就能进行更多更深层次的搜索。

这就是传说中的博弈树跟α-β剪枝的原理了。

你可能感兴趣的:(大数据)