Python秒算24点实现及原理详解


Posted in Python onJuly 29, 2019

什么是24点

我们先来约定下老王和他媳妇玩的24点规则:给定4个任意数字(0-9),然后通过+,-,*,/,将这4个数字计算出24。

小时候玩的都是这个规则,长大了才有根号,才有各种莫名其妙的高级算法,不好玩了,因为我不会。

可能有人会觉得很简单,但是真的简单吗?

比如:

8,3,3,3
7,3,3,3
你能一眼看出来答案吗?好像真的可以……

大致思路

这样想,将四个数字进行全排列,在他们之间添加运算符号。

运算符我们需要进行排列组合,因为只有四个数字,所以只需要三个运算符,而且算法符可能会重复,比如三个都是+。

再遍历四个数字的全排列,对每一组数字而言,遍历所有组合的操作符。最后将数字和操作符进行拼接运算,就可以得到最终结果了。

演示环境

操作系统:windows10

python版本:python 3.7

代码编辑器:pycharm 2018.2

使用模块:math,itertools, collections.abc

具体代码

1、首先我们对所有数字进行去全排列,这里我们使用 itertools.permutations 来帮助我们完成。

iertools.permutations 用法演示

from itertools import permutations

data_list = permutations([1,2,3,4],2)
for data in data_list:
print(data)

结果显示

(1, 2)
(1, 3)
(1, 4)
(2, 1)
(2, 3)
(2, 4)
(3, 1)
(3, 2)
(3, 4)
(4, 1)
(4, 2)
(4, 3)

permutations 第一个参数是接收一个课迭代的对象,第二个参数指定每次排列时从课迭代对象中选着几个字符进行排列。也可以不传入第二个参数,那么默认就是可迭代对象的长度。并且返回一个生成器。

所以我们需要对所有数字进行全排列,就可以像下面这样写:

def get_all_data_sequence(data_iter):
 return permutations(data_iter)

2、然后我们需要拿到所有的操作运算符的所有组合方式。这里我们就会使用 itertools.product 函数了。

itertools.product 用法演示

from itertools import product

sequence1 = product('ABCD','xy')
sequence2 = product([0,1],repeat=3)

for sequence in sequence1:
 print(sequence)

print('-'*30)

for sequence in sequence2:
 print(sequence)

结果显示

('A','x')
('A','y')
('B','x')
('B','y')
('C','x')
('C','y')
('D','x')
('D','y')
------------------------------
(0, 0, 0)
(0, 0, 1)
(0, 1, 0)
(0, 1, 1)
(1, 0, 0)
(1, 0, 1)
(1, 1, 0)
(1, 1, 1)

itertools.product,返回传入所有序列中笛卡尔积的元祖,repeat参数表示传入序列的重复次数。返回的是一个生成器。

那么获取所有的操作运算符就可以通过这个函数来获取了

def get_all_operations_sequence():
 operations = ['+','-','*','/']
 return product(operations,repeat=3)

3、现在我们已经拿到了所有可能组合的操作符和数字了,接下来就需要对他们进行拼接了。然后执行运算。

这一步操作我们会用到 itertools.zip_longest() 和 itertools.chain.form_iterable() 函数。

itertools.zip_longest() 用法演示

data = zip_longest([1,2,3,4],['*','-','+'],fillvalue='')
for value in data:
 print(value)

结果显示

(1, '*')
(2, '-')
(3, '+')
(4, '')

zip_longest() 其实和 python 内置的 zip() 函数用法差不多,只是 zip_longest 是以最长的一个序列为基准,缺失值就使用 fillvalue 参数的值进行填充

itertools.chain.form_iterable() 用法演示

data = zip_longest([1,2,3,4],['*','-','+'],fillvalue='')
data_chain = chain.from_iterable(data)
for value in data_chain: 
 print(value)

结果显示

1
*
2
-
3
+
4

这里的data是什么样的大家知道了吧,然后我们将data传入 chain.form_iterable() 中,它就能将里面的值依次拿出来。

了解了这两个函数之后,那么我们就可以开始拼接数字和操作运算符了。

def calculate(self):
 '''
 计算值,返回对应的表达式和值
 :return: 
 ''' 
 for data_sequence in get_all_data_sequence():  
  operation_sequences = get_all_operation_sequence()  
  for operation_sequence in operation_sequences:   
   value = zip_longest(data_sequence, operation_sequence, 
  fillvalue='')   
   value_chain = chain.from_iterable(value)   
   calculate_str = ''   
   # 对得到的字符进行拼接成为表达式 calculate_str
   for _ in value_chain:    
    calculate_str += _   
   try:
    result = eval(calculate_str
   # 处理被除数可能为零的情况,然后就直接跳过这次循环
   except ZeroDivisionError:
    continue
   if math.isclose(result, 24):     
    return calculate_str,result
 return None,None

代码分析

1、eval() 函数,接受一个字符串,能让这个字符串当成 python 代码运行,返回运行的结果。

2、math.isclose():为什么这里需要使用 math.isclose() ,而不是直接使用==运算符呢?这是因为最后算出来的表达式可能有精度问题,例如23.9...或者24.0...等数字,所以我们就需要使用math.isclose()函数来帮助我们判断两个数字是否相等了,这个函数就有一个精度范围。这样出现上面情况的时候,我们也能匹配得到条件了。

我们运行代码,然后测试代码是否能达到我们的需求。

首先我们测试1,2,3,4四个数字,

程序出来了结果 1*2*3*4 24

看来好像我们写的代码是正确的

我们再来测试一组数据8,8,3,3.

嗯?我们并没有得到结果?这四个数字不能运算出24吗?

8 / ( 3 - 8 / 3 ) 这样组合可以吧,为什么没有算出来这种结果呢?

这是因为我们没有考虑括号的原因。括号是可以改变运算优先级的。所以我们得把括号考虑进去。

那么想一下括号最多可以有几个呢?怎样给我们的表达式添加括号呢?

在4个数字的运算中,括号最多只能有三个。

并且,在这里,我们使用一种简单的方法添加括号,我们把所有可能出现括号的情况全部罗列出来,然后在将得到的运算表达式拼接进去。

可能大家会觉得罗列出所有括号出现的情况不现实,因为有很多情况

其实不然,当我们去罗列的时候,你就会发现,只有11种情况。

FORM_STRS = [
 # 数字 运算符 数字 运算符 数字 运算符 数字
 # 一个括号 的情况
 '(%s %s %s) %s %s %s %s',
 '(%s %s %s %s %s) %s %s',
 '(%s %s %s %s %s %s %s)',
 '%s %s (%s %s %s) %s %s',
 '%s %s (%s %s %s %s %s)',
 '%s %s %s %s (%s %s %s)',
 # 两个括号 的情况
 '(%s %s %s) %s (%s %s %s)',
 '( (%s %s %s) %s %s) %s %s',
 '( %s %s (%s %s %s)) %s %s',
 '%s %s ((%s %s %s) %s %s)',
 '%s %s (%s %s (%s %s %s))',
 # 三个括号是重复的,就不用罗列出来了
]

然后我们对得到的表达式在进行遍历拼接,然后我们再运算表达式。

这样我们就能得出正确的结果了

代码写完了,终于可以开始和媳妇,哦不,老王家的媳妇玩起来了

代码已全部上传至Github

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python根据经纬度计算距离示例
Feb 16 Python
Python模拟登陆实现代码
Jun 14 Python
Python2.X/Python3.X中urllib库区别讲解
Dec 19 Python
Python中pip更新和三方插件安装说明
Jul 08 Python
Django网络框架之HelloDjango项目创建教程
Jun 06 Python
对Python中画图时候的线类型详解
Jul 07 Python
Django对数据库进行添加与更新的例子
Jul 12 Python
将Pytorch模型从CPU转换成GPU的实现方法
Aug 19 Python
Python使用uuid库生成唯一标识ID
Feb 12 Python
Python datetime模块使用方法小结
Jun 18 Python
Python3基于print打印带颜色字符串
Jul 06 Python
用Python自动清理电脑内重复文件,只要10行代码(自动脚本)
Jan 09 Python
django之状态保持-使用redis存储session的例子
Jul 28 #Python
django 通过URL访问上传的文件方法
Jul 28 #Python
django使用admin站点上传图片的实例
Jul 28 #Python
Python中变量的输入输出实例代码详解
Jul 28 #Python
对django中foreignkey的简单使用详解
Jul 28 #Python
解决django 新增加用户信息出现错误的问题
Jul 28 #Python
Django使用Channels实现WebSocket的方法
Jul 28 #Python
You might like
深入理解require与require_once与include以及include_once的区别
2013/06/05 PHP
PHP截断标题且兼容utf8和gb2312编码
2013/09/22 PHP
jquery JSON的解析方式
2009/07/25 Javascript
解决jquery .ajax 在IE下卡死问题的解决方法
2009/10/26 Javascript
Javascript中的isNaN函数使用说明
2011/11/10 Javascript
jQuery让控件左右移动的三种实现方法
2013/09/08 Javascript
jQuery常用数据处理方法小结
2015/02/20 Javascript
js制作带有遮罩弹出层实现登录注册表单特效代码分享
2015/09/05 Javascript
Bootstrap导航栏各元素操作方法(表单、按钮、文本)
2015/12/28 Javascript
js实现不重复导入的方法
2016/03/02 Javascript
AngularJS入门(用ng-repeat指令实现循环输出
2016/05/05 Javascript
深入学习js瀑布流布局
2016/10/14 Javascript
详解angular中通过$location获取路径(参数)的写法
2017/03/21 Javascript
JS实现的数组去除重复数据算法小结
2017/11/17 Javascript
浅谈React之状态(State)
2018/09/19 Javascript
django使用channels2.x实现实时通讯
2018/11/28 Javascript
vue路由前进后退动画效果的实现代码
2018/12/10 Javascript
微信小程序报错: thirdScriptError的错误问题
2020/06/19 Javascript
Python挑选文件夹里宽大于300图片的方法
2015/03/05 Python
Python for Informatics 第11章之正则表达式(四)
2016/04/21 Python
Django中日期处理注意事项与自定义时间格式转换详解
2018/08/06 Python
Python多线程应用于自动化测试操作示例
2018/12/06 Python
Python使用pandas对数据进行差分运算的方法
2018/12/22 Python
对python列表里的字典元素去重方法详解
2019/01/21 Python
Python编程快速上手——Excel到CSV的转换程序案例分析
2020/02/28 Python
python matplotlib包图像配色方案分享
2020/03/14 Python
python语言的优势是什么
2020/06/17 Python
详解python tkinter 图片插入问题
2020/09/03 Python
英国在线花园中心:You Garden
2018/06/03 全球购物
SheIn沙特阿拉伯:女装在线
2020/03/23 全球购物
介绍一下Java中的Class类
2015/04/10 面试题
"引用"与指针的区别是什么
2016/09/07 面试题
大学生职业生涯规划书的基本内容
2014/01/06 职场文书
合伙经营协议书范本
2014/04/18 职场文书
莫言诺贝尔获奖演讲稿
2014/05/21 职场文书
vue使用wavesurfer.js解决音频可视化播放问题
2022/04/04 Vue.js