只用一行代码就能搞定,博弈论究竟是什么神仙算法?

简介: 云栖号资讯:【点击查看更多行业资讯】在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来! 博弈论是一门很庞大的学科,它算是数学的一个分支,也和运筹学甚至是经济学有关。虽然它严格说起来并不是算法领域的内容,但是有不少关于博弈论有趣的算法和问题。

云栖号资讯:【点击查看更多行业资讯
在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来!


博弈论是一门很庞大的学科,它算是数学的一个分支,也和运筹学甚至是经济学有关。虽然它严格说起来并不是算法领域的内容,但是有不少关于博弈论有趣的算法和问题。关于博弈的相关理论从很早就已经有了雏形,但是正式构建理论成为一门学科是从计算机之父冯诺依曼开始的。从这点上来说也和计算机有点关系。

今天我们来聊聊博弈论当中最简单的巴什博奕(Bash Game)。

报数问题

说到巴什博奕就不能不提报数问题,它实在是太经典了,以至于我觉得你很有可能也听说过。题目是这样的,说是A和B两个人一起玩一个报数游戏,两个人轮流报数,每次最少报一个数,最多报5个数,第一个报到100的人获胜。如果你是先手的A,应该采取什么策略?

如果之前没有思考过这个问题或者是了解过博弈论的话,估计你可能会觉得这个问题很复杂,也很困难,应该没有什么好的办法。因为两个人每次都可以做好几种选择,每一种选择又会带来不同的选择,这样依次交错会带来大量的可能性。在这种情况下,似乎很难想出一个算法来解决问题。

报到100看不出来,我们缩小一下,如果报到6的人获胜呢?是不是就很明显了,先手的A不论采取什么策略都一定输。因为它不论报几个数,B都可以直接报到6。

既然报6个数的时候A一定必输,那么可以推导得到报的数如果是6的倍数A都一定必输。假设它在某一轮当中报了i,对方只要报6-i就行了。这样重复若干次之后最后一定会剩下6,那么就回到了上面说的最简单的情况。

假设我们开发出了一个state函数可以计算某个状态先手是必胜还是必败,我们用1表示先手必胜,0表示必败。那么显然state(0) = 0,state(6) = 0。由于不论先手在每一轮当中报几,后手都可以控制报一个和它加起来等于6的数,所以可以得到state(n) = state(n-6)。于是,我们可以推导出state(6n) = 0。

由于先手每次可以报1-5个数,当他面临一个6n+k的局面的时候,只要k不等于0。那么他就报k个数,就可以让局面转化成6n的必败局面给B。所以可以知道,除了6n的局面之外的所有局面都是先手必胜的。

我们用代码实现的话就只有一行:

def win_or_lose(n):
    return n % 0 != 0

博弈论的精髓在于对问题的分析,体现在代码上就是思维比较复杂,但是代码极为精简。

报数这个问题比较直观,所以找找规律仔细想想也是可以猜出答案来的。但如果我们包装一下,可能就不一样了。

打牌问题

这题源自HDU1847,题面是两个人打牌,一共有n张牌,双方轮流抓牌。规定每人每次抓牌的数量必须是2的幂,最后抓完排的人获胜。假设两人都是极端聪明,请问最后会是谁获胜。

假设两个人极端聪明,这是博弈论问题的前提,也就是说两个人都会采取最优策略。没有这个前提,就无法使用博弈论进行分析了,因为它就不再是单纯的数学问题了。

和上面的题目相比,这题变得复杂了。因为每个人采取的策略数量变了,之前是严格限制了只有5种可能,现在则变成了无数种。但其实这里使用了障眼法,藏了一个trick。

我们首先把2的幂都列出来,从2的0次方开始,分别是1, 2, 4, 8...。看到这个1和2不知道大家有没有什么想法,其实如果你稍微了解一点数论的话就会知道,2的幂一定不能被3整除。也就是说2的幂模3的结果只会有两种,也就是1或者是2。所以这道题其实和上面一题是一样的, 我们拿2的几次幂不重要,重要的是拿的数模3之后余下的几。

state(0) = 0,因为对方已经拿完了。那么state(1) = state(2) = 1,只剩一张或者两张牌的时候可以一次全拿完。state(3) = 0,因为无法一次拿完。我们推广可以得到state(3n) = 0。也就是3的倍数对于先手是必败的。由于我们刚才说了,2的幂模3一定是1或者是2。所以可以得到,对于先手来说,只要面临的状态不是3的倍数,就一定必胜。因为他一定可以找到一个2的幂,使得拿完之后留下3的倍数给对方。

分析完了之后,代码又只有一行:

def win_or_lose(n):
    return n % 3 != 0

总结

巴什博奕的问题很简单,一旦摸清楚了套路之后,这一系列类似的问题都手到擒来。但是要注意的是,面临巴什博奕的问题,我们不能只是简单地理解成是凑成一个数,或者是找到一个必胜或者必败的策略。而是要从状态的角度去分析它,究竟什么样的状态是必胜或者是必败的,状态之间又存在什么样的转移关系。

从这点上来看似乎又有点动态规划的意思,但不一样的是动态规划算法解决的都是边界有限的问题。而博弈论当中有的时候策略或者是状态可能是无限的,但是两者的确有相通的部分。巴什博奕只是博弈论算法当中最简单的算法,后面我们还会继续研究其他更复杂一些的博弈论问题。

【云栖号在线课堂】每天都有产品技术专家分享!
课程地址:https://yqh.aliyun.com/live

立即加入社群,与专家面对面,及时了解课程最新动态!
【云栖号在线课堂 社群】https://c.tb.cn/F3.Z8gvnK

原文发布时间:2020-06-13
本文作者:承志
本文来自:“掘金”,了解相关信息可以关注“掘金”

相关文章
|
10天前
|
算法 测试技术 开发者
在Python开发中,性能优化和代码审查至关重要。性能优化通过改进代码结构和算法提高程序运行速度,减少资源消耗
在Python开发中,性能优化和代码审查至关重要。性能优化通过改进代码结构和算法提高程序运行速度,减少资源消耗;代码审查通过检查源代码发现潜在问题,提高代码质量和团队协作效率。本文介绍了一些实用的技巧和工具,帮助开发者提升开发效率。
14 3
|
9天前
|
分布式计算 Java 开发工具
阿里云MaxCompute-XGBoost on Spark 极限梯度提升算法的分布式训练与模型持久化oss的实现与代码浅析
本文介绍了XGBoost在MaxCompute+OSS架构下模型持久化遇到的问题及其解决方案。首先简要介绍了XGBoost的特点和应用场景,随后详细描述了客户在将XGBoost on Spark任务从HDFS迁移到OSS时遇到的异常情况。通过分析异常堆栈和源代码,发现使用的`nativeBooster.saveModel`方法不支持OSS路径,而使用`write.overwrite().save`方法则能成功保存模型。最后提供了完整的Scala代码示例、Maven配置和提交命令,帮助用户顺利迁移模型存储路径。
|
22天前
|
存储 缓存 算法
如何通过优化算法和代码结构来提升易语言程序的执行效率?
如何通过优化算法和代码结构来提升易语言程序的执行效率?
|
23天前
|
搜索推荐
插入排序算法的讲解和代码
【10月更文挑战第12天】插入排序是一种基础的排序算法,理解和掌握它对于学习其他排序算法以及数据结构都具有重要意义。你可以通过实际操作和分析,进一步深入了解插入排序的特点和应用场景,以便在实际编程中更好地运用它。
|
3月前
|
机器学习/深度学习 人工智能 自然语言处理
【自然语言处理】TF-IDF算法在人工智能方面的应用,附带代码
TF-IDF算法在人工智能领域,特别是自然语言处理(NLP)和信息检索中,被广泛用于特征提取和文本表示。以下是一个使用Python的scikit-learn库实现TF-IDF算法的简单示例,并展示如何将其应用于文本数据。
254 65
|
15天前
|
缓存 分布式计算 监控
优化算法和代码需要注意什么
【10月更文挑战第20天】优化算法和代码需要注意什么
15 0
|
1月前
|
算法 Java 测试技术
数据结构 —— Java自定义代码实现顺序表,包含测试用例以及ArrayList的使用以及相关算法题
文章详细介绍了如何用Java自定义实现一个顺序表类,包括插入、删除、获取数据元素、求数据个数等功能,并对顺序表进行了测试,最后还提及了Java中自带的顺序表实现类ArrayList。
18 0
|
2月前
|
机器学习/深度学习 存储 算法
经典算法代码
这段代码展示了多个经典算法,包括:穷举法解决“百钱买百鸡”问题;递推法计算“猴子吃桃”问题;迭代法求解斐波那契数列及折纸高度超越珠峰的问题。同时,还提供了希尔排序算法实现及披萨票务订购系统和汉诺塔问题的链表存储解决方案。每部分通过具体案例解释了算法的应用场景与实现方法。
30 3
|
3月前
|
机器学习/深度学习 人工智能 算法
【人工智能】传统语音识别算法概述,应用场景,项目实践及案例分析,附带代码示例
传统语音识别算法是将语音信号转化为文本形式的技术,它主要基于模式识别理论和数学统计学方法。以下是传统语音识别算法的基本概述
71 2
|
3月前
|
搜索推荐 算法 Java