Python基础算法案例:24点纸牌游戏算法
作者:长行
时间:2020.05.14
Github原文: Week-03/Example-0303
目标要求
对于任意给定的四张扑克牌,计算是否有赢得24点游戏的方法(即使用加、减、乘、除四则运算凑成24的方法);如果有的话,列出所有可能的方法。
【24点游戏规则】
在大小王以外的52张牌中,任意抽取其中4张牌。如果通过加、减、乘、除四则运算(可加括号)的方法,将抽到的4张牌算成24,则为胜利;每张牌都必须使用,且只能使用一次。
第一种解法
依据游戏规则,我们可以想到如下解决思路:使用枚举的方法,将所有的计算方法都枚举出来,将四张扑克牌的数字代入到所有的计算方法中得出结果,如果结果为24则为解。
由此,我们得到了第一种解法。在具体实现中:将所有可能的四则运算组合和所有可能的括号组合合并在一起,由此生成所有可能的算式组合。计算某一个牌组时,先计算所有该牌组所有可能的组合方式,并将所有的组合方式带入所有可能的算式组合求解。
import itertoolsclass CardGaming:def __init__(self):self.formula_list = list() # 存储所有可能的算式for marks in itertools.product(["+", "-", "*", "/"], repeat=3):for bracket in ["{0}%s{1}%s{2}%s{3}", "({0}%s{1})%s{2}%s{3}", "({0}%s{1}%s{2})%s{3}","{0}%s({1}%s{2})%s{3}","{0}%s({1}%s{2}%s{3})", "({0}%s{1})%s({2}%s{3})", "{0}%s{1}%s({2}%s{3})"]:self.formula_list.append((bracket % marks))def solve(self, card_probability):answer = []for card_order in set(itertools.permutations(card_probability, 4)): # 遍历所有可能的不同卡牌顺序(最多24种可能)for formula in self.formula_list: # 遍历所有可能的算式(448种可能)final_formula = formula.format(*card_order)try:if round(eval(final_formula), 3) == 24:answer.append(final_formula)except ZeroDivisionError:continuereturn answerif __name__ == "__main__":print(CardGaming().solve((3, 3, 8, 8))) # 输出: 8/(3-8/3)
当前代码在计算每一个牌组的答案时,都需要遍历43×7=4484^3\times{7}=44843×7=448种算式和最多A44=24A^4_4=24A44=24种卡牌顺序,即处理最多448×24=10752448\times{24}=10752448×24=10752种可能性。使用这个解法计算所有可能的扑克牌组合(共计134=2856113^4=28561134=28561种解法),需要1906秒(I7 7700,8GB)。
第二种解法
在第一种解法中,计算每一个牌组的答案时,处理的可能性中有很多重复的情况,例如“A+B-C+D”、“D-C+B+A”、“D+A-C+B“等。这就极大地拖累了我们的运算速度。但是,要在第一种解法的基础上来合并这些不同的情况,需要同时考虑符号、括号和卡牌顺序,十分复杂。
因此,我们可以从另外一个角度来解决这个问题。
通过观察我们可以发现,无论什么算式,本质上都是按着一定的顺序,对4张扑克牌的数值进行三次运算;而每一次运算,都是从尚未用过的扑克牌以及之前的运算结果中选择2个进行运算。所以,我们可以将所有算式归纳为:
从4张牌中任意抽取2个进行任意运算,将未抽取的2张牌和运算结果组合成包含3个数值的新列表;在新列表中任意抽取2个进行任意运算,将未抽取的1张牌和运算结果组成包含2个数值的新列表;对新列表中的2个数值进行任意运算得出结果,如果结果为24则为解。
由此,我们得到了第二种算法。在具体实现中,我们主要注意如下几点:
- 因为不再枚举算式,所以我们也不再需要使用低效的
eval()
函数运行算式。 - 因为如果在运算过程中生成算式,会增加很多运算量,所以我们只在求出解后反向生成解的算式(哪怕这样生成算式会更困难,但是需要生成的次数大大减少)。
def solve(card_probability):card_probability = list(card_probability) # 生成临时列表answer = []for combine_1 in set(itertools.combinations(card_probability, 2)): # 在临时列表的4个数中任意抽取2个数for answer_1 in all_maybe_count_answer(combine_1[0], combine_1[1]):card_list_1 = copy.deepcopy(card_probability)card_list_1.remove(combine_1[0]) # 从临时列表移除抽到的数1card_list_1.remove(combine_1[1]) # 从临时列表移除抽到的数2card_list_1.append(answer_1) # 添加计算结果到临时列表for combine_2 in set(itertools.combinations(card_list_1, 2)): # 在临时列表的3个数中任意抽取2个数for answer_2 in all_maybe_count_answer(combine_2[0], combine_2[1]):card_list_2 = copy.deepcopy(card_list_1)card_list_2.remove(combine_2[0]) # 从临时列表移除抽到的数1card_list_2.remove(combine_2[1]) # 从临时列表移除抽到的数2card_list_2.append(answer_2) # 添加计算结果到临时列表for combine_3 in set(itertools.combinations(card_list_2, 2)): # 抽取临时列表剩下的2个数for answer_3 in all_maybe_count_answer(combine_3[0], combine_3[1]):if round(answer_3, 3) == 24:answer.append(total_formula(card_probability, combine_1, answer_1, combine_2,answer_2, combine_3, answer_3)) # 生成完整算式return answerif __name__ == "__main__":start_time = time.time()for cards in list(itertools.product([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], repeat=4)):solve(cards)print("计算时间:", time.time() - start_time)
(其中all_maybe_count_answer
函数计算两个参数进行四则运算的所有可能结果;total_formula
函数依据中间变量生成完整计算公式)
运行结果:
计算时间: 136.27595901489258
这种接法在第一次四则运算时,有C42=6C^2_4=6C42=6种抽取结果,有6种运算结果(减法和除法因顺序不同有2个结果);在第二次四则运算时,有C32=3C^2_3=3C32=3种抽取结果,有6种运算结果;在第三次四则运算时,有C22=1C^2_2=1C22=1种抽取结果,有6种运算结果。
因此,这种算法在求一个扑克牌组的解时,仅需要考虑C42×6×C32×6×C22×6=3888{C^2_4}\times{6}\times{C^2_3}\times{6}\times{C^2_2}\times{6}=3888C42×6×C32×6×C22×6=3888种可能性。使用这个解法计算所有可能的扑克牌组合(共计134=2856113^4=28561134=28561种解法),需要136秒(I7 7700,8GB),比第一种解法快了10倍以上。
Python基础算法案例:24点纸牌游戏算法相关推荐
- 24点游戏 java实现_java实现24点纸牌游戏
本文题目为大家分享了java实现24点纸牌游戏的具体代码,供大家参考,具体内容如下 题目 24点游戏是经典的纸牌益智游戏. 常见游戏规则: 从扑克中每次取出4张牌.使用加减乘除,第一个能得出24者为赢 ...
- 24点纸牌游戏,c语言实现
(此程序参考https://blog.csdn.net/weixin_41258179/article/details/82901853,并做出一些改进) 一.题目要求: 24点游戏规则: 从扑克中每 ...
- Python基础语法案例(Fibonacci):选择结构、循环结构、异常处理结构、代码优化
推荐图书: <Python程序设计基础(第2版)>,ISBN:9787302490562,董付国,清华大学出版社,第16次印刷,清华大学出版社2019年度畅销图书 图书购买链接(京东): ...
- 算法学习 2.3纸牌游戏——小猫钓鱼
这是一章对前面的队列,栈的操作进行运用的一个实例.玩家A和玩家B玩纸牌游戏小猫钓鱼,游戏规则如下: 一副纸牌,平均分成两份,每人拿一份(这里就用数字代替了). 玩家A先拿出一张纸牌放在桌子上,玩家B再 ...
- 跳一跳j算法ava代码_麻将游戏算法深入解析及实现代码
麻将游戏算法深入解析及实现代码 这两天为了工具箱的完善,整理了这些年引擎开发的一些资料,无意中发现06年写的一个麻将算法,编译运行了一下,还是有点意思的,拿出来整理一下分享给大家. 麻将是一种大家最喜 ...
- Python基础编程案例:简单的井字棋游戏设计与制作
本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 前言 python井字棋游戏虽然看上去非常简陋,但是却非常值得学习. 先看怎么玩 ...
- [Python私活案例]24行代码,轻松赚取400元,运用Selenium爬取39万条数据
今天分享一单来自金主爸爸的私单,运用简单的爬虫技巧,可以有效的规避反爬机制,正所谓"你有张良计,我有过云梯".这个案例也很好的体现了python语音的优势,规避了非常复杂的底层逻辑 ...
- python决策树分类案例_python实现决策树分类算法
本文实例为大家分享了python实现决策树分类算法的具体代码,供大家参考,具体内容如下 1.概述 决策树(decision tree)--是一种被广泛使用的分类算法. 相比贝叶斯算法,决策树的优势在于 ...
- 数据挖掘算法案例python_《常用数据挖掘算法总结及Python实现》[5.1MB]PDF影印版下载-码农之家...
<常用数据挖掘算法总结及Python实现>是一本数据挖掘相关的电子书资源,介绍了关于数据挖掘.算法总结.Python方面的内容,格式为PDF,资源大小5.1 MB,由debao9765 提 ...
最新文章
- 用eclipse阅读编辑android和kernel,uboot的源代码
- java并发编程之Semaphore
- 天合汽车安全技术(张家港)有限公司
- JavaScript之闭包
- 【软件开发底层知识修炼】二十三 ABI-应用程序二进制接口三之深入理解函数栈帧的形成与摧毁
- 机器学习-吴恩达-笔记-10-降维
- GIMP制作电子签名
- quartus驱动无法识别分析
- 解决iPhone、iPad的home按键不灵敏
- 60-硅谷课堂6-硅谷课堂-公众号消息和微信授权-- 笔记
- vue+videojs视频播放、视频切换、视频断点分段上传
- 世界上第一个微处理器真的是Intel 4004吗?其实这是个很复杂的故事…
- 世界最大射电望远镜(Arecibo)用于探测地外文明
- 关于实现3D立体旋转效果的轮播视图
- watershed分水岭详解
- 云呐数据备份|什么是离线磁带设备
- 高数焦虑?这个假期,暗暗发力,2023年“兔”飞猛进
- 数据分析-豆瓣电影Top250
- ECU扫盲篇——什么是ECU?
- 敏捷开发与中医理论系列之二:古法教学(软件教育,松结对编程,师徒制度)