作者:长行

时间: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点纸牌游戏算法相关推荐

  1. 24点游戏 java实现_java实现24点纸牌游戏

    本文题目为大家分享了java实现24点纸牌游戏的具体代码,供大家参考,具体内容如下 题目 24点游戏是经典的纸牌益智游戏. 常见游戏规则: 从扑克中每次取出4张牌.使用加减乘除,第一个能得出24者为赢 ...

  2. 24点纸牌游戏,c语言实现

    (此程序参考https://blog.csdn.net/weixin_41258179/article/details/82901853,并做出一些改进) 一.题目要求: 24点游戏规则: 从扑克中每 ...

  3. Python基础语法案例(Fibonacci):选择结构、循环结构、异常处理结构、代码优化

    推荐图书: <Python程序设计基础(第2版)>,ISBN:9787302490562,董付国,清华大学出版社,第16次印刷,清华大学出版社2019年度畅销图书 图书购买链接(京东): ...

  4. 算法学习 2.3纸牌游戏——小猫钓鱼

    这是一章对前面的队列,栈的操作进行运用的一个实例.玩家A和玩家B玩纸牌游戏小猫钓鱼,游戏规则如下: 一副纸牌,平均分成两份,每人拿一份(这里就用数字代替了). 玩家A先拿出一张纸牌放在桌子上,玩家B再 ...

  5. 跳一跳j算法ava代码_麻将游戏算法深入解析及实现代码

    麻将游戏算法深入解析及实现代码 这两天为了工具箱的完善,整理了这些年引擎开发的一些资料,无意中发现06年写的一个麻将算法,编译运行了一下,还是有点意思的,拿出来整理一下分享给大家. 麻将是一种大家最喜 ...

  6. Python基础编程案例:简单的井字棋游戏设计与制作

    本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 前言 python井字棋游戏虽然看上去非常简陋,但是却非常值得学习. 先看怎么玩 ...

  7. [Python私活案例]24行代码,轻松赚取400元,运用Selenium爬取39万条数据

    今天分享一单来自金主爸爸的私单,运用简单的爬虫技巧,可以有效的规避反爬机制,正所谓"你有张良计,我有过云梯".这个案例也很好的体现了python语音的优势,规避了非常复杂的底层逻辑 ...

  8. python决策树分类案例_python实现决策树分类算法

    本文实例为大家分享了python实现决策树分类算法的具体代码,供大家参考,具体内容如下 1.概述 决策树(decision tree)--是一种被广泛使用的分类算法. 相比贝叶斯算法,决策树的优势在于 ...

  9. 数据挖掘算法案例python_《常用数据挖掘算法总结及Python实现》[5.1MB]PDF影印版下载-码农之家...

    <常用数据挖掘算法总结及Python实现>是一本数据挖掘相关的电子书资源,介绍了关于数据挖掘.算法总结.Python方面的内容,格式为PDF,资源大小5.1 MB,由debao9765 提 ...

最新文章

  1. 用eclipse阅读编辑android和kernel,uboot的源代码
  2. java并发编程之Semaphore
  3. 天合汽车安全技术(张家港)有限公司
  4. JavaScript之闭包
  5. 【软件开发底层知识修炼】二十三 ABI-应用程序二进制接口三之深入理解函数栈帧的形成与摧毁
  6. 机器学习-吴恩达-笔记-10-降维
  7. GIMP制作电子签名
  8. quartus驱动无法识别分析
  9. 解决iPhone、iPad的home按键不灵敏
  10. 60-硅谷课堂6-硅谷课堂-公众号消息和微信授权-- 笔记
  11. vue+videojs视频播放、视频切换、视频断点分段上传
  12. 世界上第一个微处理器真的是Intel 4004吗?其实这是个很复杂的故事…
  13. 世界最大射电望远镜(Arecibo)用于探测地外文明
  14. 关于实现3D立体旋转效果的轮播视图
  15. watershed分水岭详解
  16. 云呐数据备份|什么是离线磁带设备
  17. 高数焦虑?这个假期,暗暗发力,2023年“兔”飞猛进
  18. 数据分析-豆瓣电影Top250
  19. ECU扫盲篇——什么是ECU?
  20. 敏捷开发与中医理论系列之二:古法教学(软件教育,松结对编程,师徒制度)

热门文章

  1. Java 转PPT为图片、PDF、SVG、XPS、ODP以及PPT和PPTX互转
  2. jQuery_02 快速入门 $作用和方法
  3. kubeadm部署k8s集群最全最详细
  4. 四次方程根式解+四次以上方程近似解的js实现代码(上)——复数类+复数常量+三角函数简表
  5. Prometheus告警规则
  6. 安卓的app在所有应用商店上架方法整理
  7. IE浏览器极限提速完全攻略(软媒原创)
  8. 线上支付同时支持支付宝和微信,关于退款问题的测试
  9. Git 分支篇之分支介绍
  10. 无显卡本地运行katago围棋程序