【五子棋实战】第2章 博弈树负值极大alpha-beta剪枝算法(一)

简介: 市面上比较常用的五子棋算法是博弈树极大极小值alpha-beta剪枝算法,该算法可以分成四个部分来讲解,它们是环环相扣的:博弈树 - 极大极小值搜索 - 负值极大法 - alpha&beta剪枝 。

  市面上比较常用的五子棋算法是博弈树极大极小值alpha-beta剪枝算法,该算法可以分成四个部分来讲解,它们是环环相扣的:博弈树 - 极大极小值搜索 - 负值极大法 -alpha&beta剪枝


博弈树


  博弈树(Game Tree)是博弈论中的一个概念,用于表示博弈过程中的各种可能走法和对应的结果。它是树结构,树的每个节点表示游戏的一个状态,每个节点的子节点表示在该状态下可能的下一步行动。


由于是树结构,在代码实现上会使用递归,再细分一下可以说是回溯法traceback。


 在博弈树中,根节点表示游戏的初始状态,而叶子节点则表示游戏结束的状态。通过遍历博弈树,可以找到最佳的行动策略或评估游戏的结果。


 对于两人对弈的游戏,博弈树的每一层交替表示不同的玩家的行动。在五子棋游戏中,轮到某一方行动时,会生成一层子节点,每个子节点代表下一步的落子位置。对于每个落子位置,继续生成下一层子节点,以此类推,直到游戏结束。


但是通常不可能一直类推到游戏结束,走3步的计算时间就很长很长了。


  举个例子,假设有一个2*2的五子棋棋盘,黑子先行,下在左上角,如下图:


image.png


 那么博弈树就如上图构建的这样。根节点为当前棋局,第1步是白子下棋,还剩下3个空位可以下;第2步又轮到黑子下棋,还剩2个空位;如此循环下去……


 我们发现对于2x2的棋盘,博弈树一共有3x2x1种路径。对于一个正常的15x15的棋盘,博弈树会有224层224!种路径。那么如何能确定哪一种路径是最优的呢?这就用到了极大极小值思想。


极大极小值搜索Minimax


 极大极小值思想是一种在博弈和人工智能领域中常用的方法。在博弈中,可以使用极大极小值搜索算法(如Minimax算法)来确定最佳的下一步行动。算法通过递归地模拟所有可能的游戏状态和对手的反应,然后选择最有利于自己的行动。


 在执行极大极小值搜索算法之前,我们需要给每个状态的棋盘进行打分,然后才能用极大极小值搜索算法来比较哪个状态的棋盘是优的。


  为棋盘打分 - 评分标准


  首先需要一个评分标准。对于五子棋来说,即如果出现了"空白白空空"这样的棋型我们要算多少分,如果出现了"白黑黑黑黑空"这样的棋型我们要算多少分。项目中设计的评分标准如下,并不区分黑白子,1为有子,0为空子:


# 棋型的评估分数
shape_score = [
    (50, (0, 1, 1, 0, 0)),
    (50, (0, 0, 1, 1, 0)),
    (200, (1, 1, 0, 1, 0)),
    (500, (0, 0, 1, 1, 1)),
    (500, (1, 1, 1, 0, 0)),
    (5000, (0, 1, 1, 1, 0)),
    (5000, (0, 1, 0, 1, 1, 0)),
    (5000, (0, 1, 1, 0, 1, 0)),
    (5000, (1, 1, 1, 0, 1)),
    (5000, (1, 1, 0, 1, 1)),
    (5000, (1, 0, 1, 1, 1)),
    (5000, (1, 1, 1, 1, 0)),
    (5000, (0, 1, 1, 1, 1)),
    (500000, (0, 1, 1, 1, 1, 0)),
    (99999999, (1, 1, 1, 1, 1))
]


这个评分标准的设计直接影响算法的效果,非常重要。而且,这里的评分标准也不是最优的,只是效果还不错,建议读者自行增加、修改去试试。


 为棋盘打分 - 如何累计分数


 累计分数也是一个大学问。


 本项目的做法是,对于每一个落白子的点A,去遍历A的横、竖、左斜、右斜的四个方向上是否有白子的形状落入评分标准,有的话就记录下最大分值和对应的形状。如果这个点A在四个方向中的某个方向已经被记录过,那么就不在纳入计算了。最后从记录的数组中找一下有没有相交的点,有的话得分加倍。


 黑子也是这样计算。


 最终棋盘的分数 = 我方颜色棋子总得分 - 敌方棋子总得分 * 权重。


所以其实白子、黑子都有参与到计算中来。权重体现了电脑的进攻性,可配置。


  极大极小值思想


  极大极小值思想的目的是为了找到博弈树最优的一条路径。


image.png


 极大极小值思想考虑的是在对手也采取最优策略的情况下,我们能够保证的最小收益或最大损失。


 在上图中,假设最后一层分数为1、2、3、4、5、6,那么它们的上一层是我方,我方取对自己有利的(取max),所以第三层还是1、2、3、4、5、6,第二层是敌方,他会取对我方最不利的分数(取min),所以是1、3、5,第一层是我方,取max=5。


 总结一下,当前黑子下完了,白子落子搜索到了第二层节点值为5的那个节点的落子坐标。


因为是回溯,所以树肯定是从下往上看。


负值极大法Negamax


  对于敌方而言,取得是最小。但是如果也让敌方取最大,只是最后加一个负号,那么不也相当于取最小吗??而且这样会简化我们的代码,使得我们的代码变得更少更优雅。于是就产生了负值极大法。


其实是使代码像天书一样抽象难看懂。。。。


 负值极大法(Negamax)是一种基于极小化搜索树的博弈算法。它基于一个基本的观察:对于一个玩家而言,最好的策略与对手的最差策略是等价的。因此,通过将对手的收益取负值,问题可以转化为一个极大化搜索问题。


 这两种算法本质上是相似的,只是表达方式不同。在Negamax算法中,通过将对手的收益取负值,将问题转化为一个极大化搜索问题;而在Minimax算法中,通过交替最大化和最小化的方式,在博弈树中搜索最佳决策。


image.png


 AI算法的厉害之处还在于你会往下递归几步,15x15的棋盘可以递归224步,有224!种路径,意味着你要从第224层往第一层回溯,估计只有超算才行了。


 通常我们递归3层就224x223x222=1108,9344个叶子节点了。于是,聪明的人对其进行了优化, 这就是alpha-beta剪枝搜索。


alpha-beta剪枝


 Alpha-beta剪枝是一种用于优化极大极小搜索(Minimax Search)算法的技术,它可以有效地减少搜索的节点数,从而提高搜索效率。Alpha-beta剪枝算法在Minimax搜索的基础上引入了两个参数:alpha和beta。


 在Minimax搜索过程中,每个玩家都追求最大化自己的收益或最小化对手的收益。在搜索的过程中,当某个节点的值超出了alpha到beta的范围时,可以通过剪枝操作来减少对该节点以及其子节点的搜索。


 具体来说,当搜索到一个节点时,会先搜索其子节点,并递归地进行下去。在递归返回之后,根据当前玩家是最大化还是最小化玩家,对alpha和beta进行更新。如果alpha的值大于等于beta的值,那么当前节点的搜索可以提前结束,不再搜索其余子节点,因为它们不会影响当前玩家的最佳选择。


 通过不断更新alpha和beta的值,可以在搜索的过程中动态地减少搜索的节点数,从而大幅度提高搜索效率。Alpha-beta剪枝算法的关键在于正确地维护alpha和beta的值,并且按照正确的顺序遍历子节点,以确保剪枝操作的正确性。


image.png


  特别重要 - 剪枝过程


 如上图,α为已知的最大值, β为已知的最小值, 因为还没搜索不知道是多少,保险起见,初始化为-∞ 和+∞。


 1 => 首先,深度优先遍历,会遍历到第四层第一个节点值为1,它的父节点属于Max层,那么把α置为1,说明这个父节点只接收>=1的值。


 2 => 接着回溯到了第二层第一个节点,由于每返回一层它们的α、β值是互换的,所以此时这个节点说它只接收<=1的值。


 3 => 接着遍历到第四层第二个节点值为2,它的父节点属于Max层,那么把α置为2,说明这个父节点只接收>=2的值。此时问题来了,第三层第二个节点>=2,第二层第一个节点<=1,产生冲突,把第三层第二个节点为根的子树剪掉,因为第三层第二个节点>=2表示我这个子树肯定是>=2了,但是它的父节点只要<=1的,那还要你干嘛?


 4 => 那么回溯到根节点,根节点现在变成>=1的了。


 5 => 接着遍历到第四层第三个节点值为3,它的父节点属于Max层,那么把α置为3,说明这个父节点只接收>=3的值。


 6 => 此时回溯到了第二层第二个节点,由于每返回一层它们的α、β值是互换的,所以此时这个节点说它只接收<=3的值。


 7 => 接着遍历到第四层第四个节点值为2,它的父节点属于Max层,那么把α置为2,说明这个父节点只接收>=2的值。而它的父节点是>=3,所以不剪枝,同时回溯,覆盖父节点的范围,变成<=2。


  8 => 那么又回溯到根节点,根节点之前是>=1的,现在被覆盖,变为>=2。


  9 => ……其他同理……


因为我图画的简陋,所以看不出来剪枝的妙处。但是可以想象一下上图被剪枝的部分,如果它们节点有很多,那么除了左子树,它们的右子树都不会再需要遍历了。


 值得注意的是,Alpha-beta剪枝算法适用于满足最优化原则的博弈问题,即一个玩家的收益等于另一个玩家的损失。它能够在保证找到最优解决方案的前提下,大大减少搜索的节点数,提高搜索效率。然而,如果搜索树的分支因子很高,或者搜索深度很大,仍然可能需要相当大的计算资源来完成搜索。因此,在实际应用中,仍然需要结合启发式评估函数等其他优化技术来进一步提高效率。


相关文章
|
7天前
|
算法 Python
算法不再难!Python分治法、贪心、动态规划实战解析,轻松应对各种算法挑战!
【7月更文挑战第8天】掌握Python算法三剑客:分治、贪心、动态规划。分治如归并排序,将大问题拆解递归解决;贪心策略在每步选最优解,如高效找零;动态规划利用子问题解,避免重复计算,解决最长公共子序列问题。实例展示,助你轻松驾驭算法!**
17 3
|
5天前
|
机器学习/深度学习 数据采集 算法
Python实现GBDT(梯度提升树)回归模型(GradientBoostingRegressor算法)项目实战
Python实现GBDT(梯度提升树)回归模型(GradientBoostingRegressor算法)项目实战
|
4天前
|
机器学习/深度学习 数据采集 算法
Python实现GBDT(梯度提升树)分类模型(GradientBoostingClassifier算法)并应用网格搜索算法寻找最优参数项目实战
Python实现GBDT(梯度提升树)分类模型(GradientBoostingClassifier算法)并应用网格搜索算法寻找最优参数项目实战
|
4天前
|
算法 搜索推荐 编译器
算法高手养成记:Python快速排序的深度优化与实战案例分析
【7月更文挑战第11天】快速排序是编程基础,以O(n log n)时间复杂度和原址排序著称。其核心是“分而治之”,通过选择基准元素分割数组并递归排序两部分。优化包括:选择中位数作基准、尾递归优化、小数组用简单排序。以下是一个考虑优化的Python实现片段,展示了随机基准选择。通过实践和优化,能提升算法技能。**
8 3
|
9天前
|
算法 JavaScript
JS 【详解】树的遍历(含深度优先遍历和广度优先遍历的算法实现)
JS 【详解】树的遍历(含深度优先遍历和广度优先遍历的算法实现)
13 0
JS 【详解】树的遍历(含深度优先遍历和广度优先遍历的算法实现)
|
5天前
|
算法 Java 开发者
Java面试题:Java内存探秘与多线程并发实战,Java内存模型及分区:理解Java堆、栈、方法区等内存区域的作用,垃圾收集机制:掌握常见的垃圾收集算法及其优缺点
Java面试题:Java内存探秘与多线程并发实战,Java内存模型及分区:理解Java堆、栈、方法区等内存区域的作用,垃圾收集机制:掌握常见的垃圾收集算法及其优缺点
8 0
|
9天前
|
机器学习/深度学习 算法 搜索推荐
一个开源且全面的C#算法实战教程
一个开源且全面的C#算法实战教程
|
18天前
|
算法
技术好文共享:算法之树表的查找
技术好文共享:算法之树表的查找
12 0
|
2天前
|
算法 数据安全/隐私保护
基于GA遗传优化算法的Okumura-Hata信道参数估计算法matlab仿真
在MATLAB 2022a中应用遗传算法进行无线通信优化,无水印仿真展示了算法性能。遗传算法源于Holland的理论,用于全局优化,常见于参数估计,如Okumura-Hata模型的传播损耗参数。该模型适用于150 MHz至1500 MHz的频段。算法流程包括选择、交叉、变异等步骤。MATLAB代码执行迭代,计算目标值,更新种群,并计算均方根误差(RMSE)以评估拟合质量。最终结果比较了优化前后的RMSE并显示了SNR估计值。
16 7
|
4天前
|
算法 数据挖掘
MATLAB数据分析、从算法到实现
MATLAB数据分析、从算法到实现