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深入学习之特殊方法与多范式
Aug 31 Python
Python3 能振兴 Python的原因分析
Nov 28 Python
Python利用多进程将大量数据放入有限内存的教程
Apr 01 Python
python脚本实现xls(xlsx)转成csv
Apr 10 Python
ubuntu环境下python虚拟环境的安装过程
Jan 07 Python
python3下使用cv2.imwrite存储带有中文路径图片的方法
May 10 Python
在python中安装basemap的教程
Sep 20 Python
浅谈python多线程和多线程变量共享问题介绍
Apr 17 Python
如何创建一个Flask项目并进行简单配置
Nov 18 Python
python实现经纬度采样的示例代码
Dec 10 Python
解决numpy数组互换两行及赋值的问题
Apr 17 Python
pytorch中的numel函数用法说明
May 13 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
WIN8.1下搭建PHP5.6环境
2015/04/29 PHP
PHP常用文件操作函数和简单实例分析
2016/06/03 PHP
php使用yield对性能提升的测试实例分析
2019/09/19 PHP
jQuery生成asp.net服务器控件的代码
2010/02/04 Javascript
JavaScript原型继承之基础机制分析
2011/08/26 Javascript
通过jQuery源码学习javascript(一)
2012/12/27 Javascript
javascript轻松实现当鼠标移开时已弹出子菜单自动消失
2013/12/29 Javascript
jQuery.extend()、jQuery.fn.extend()扩展方法示例详解
2014/05/08 Javascript
如何判断微信内置浏览器(通过User Agent实现)
2014/09/01 Javascript
自定义刻度jQuery进度条及插件
2015/09/02 Javascript
浅谈javascript基础之客户端事件驱动
2016/06/10 Javascript
如何使用Vuex+Vue.js构建单页应用
2016/10/27 Javascript
利用node.js如何搭建一个简易的即时响应服务器
2017/05/28 Javascript
Angular 4 依赖注入学习教程之FactoryProvider的使用(四)
2017/06/04 Javascript
javascript判断一个变量是数组还是对象
2019/04/10 Javascript
uniapp实现横向滚动选择日期
2020/10/21 Javascript
python 正则表达式 概述及常用字符
2009/05/04 Python
Python的Flask框架应用调用Redis队列数据的方法
2016/06/06 Python
将TensorFlow的模型网络导出为单个文件的方法
2018/04/23 Python
python 对dataframe下面的值进行大规模赋值方法
2018/06/09 Python
python的pip安装以及使用教程
2018/09/18 Python
说说如何遍历Python列表的方法示例
2019/02/11 Python
Python这样操作能存储100多万行的xlsx文件
2019/04/16 Python
Charles & Colvard官网:美国莫桑石品牌
2019/06/05 全球购物
俄罗斯家居用品购物网站:Евродом
2020/11/21 全球购物
通信工程毕业生自荐信
2013/11/01 职场文书
先进班级集体事迹材料
2014/01/30 职场文书
党员公开承诺书范文
2014/03/25 职场文书
汽车维修专业自荐书
2014/05/26 职场文书
企业活动策划方案
2014/06/02 职场文书
4s店活动策划方案
2014/08/25 职场文书
大学生联谊活动策划书(光棍节)
2014/10/10 职场文书
个人优缺点总结
2015/02/28 职场文书
工厂门卫岗位职责
2015/04/13 职场文书
任长霞观后感
2015/06/16 职场文书
病假证明模板
2015/06/19 职场文书