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中最常用的操作列表的几种方法归纳
Apr 24 Python
python使用正则表达式替换匹配成功的组
Nov 17 Python
python如何在循环引用中管理内存
Mar 20 Python
使用python 3实现发送邮件功能
Jun 15 Python
python调用c++传递数组的实例
Feb 13 Python
pycharm实现在子类中添加一个父类没有的属性
Mar 12 Python
Python web如何在IIS发布应用过程解析
May 27 Python
python中数字是否为可变类型
Jul 08 Python
python ssh 执行shell命令的示例
Sep 29 Python
python绕过图片滑动验证码实现爬取PTA所有题目功能 附源码
Jan 06 Python
Python实现socket库网络通信套接字
Jun 04 Python
Python中np.random.randint()参数详解及用法实例
Sep 23 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
php面向对象全攻略 (十二) 抽象方法和抽象类
2009/09/30 PHP
php文件上传你必须知道的几点
2015/10/20 PHP
jQuery插件jPaginate实现无刷新分页
2015/05/04 Javascript
js中的触发事件对象event.srcElement与event.target详解
2017/03/15 Javascript
基于Vue实现页面切换左右滑动效果
2020/06/29 Javascript
微信小程序实现跑马灯效果完整代码(附效果图)
2018/05/30 Javascript
微信小程序实现购物页面左右联动
2019/02/15 Javascript
Vue.js实现tab切换效果
2019/07/24 Javascript
layer ui 导入文件之前传入数据的实例
2019/09/23 Javascript
Vue axios 跨域请求无法带上cookie的解决
2020/09/08 Javascript
vue如何使用rem适配
2021/02/06 Vue.js
Python cx_freeze打包工具处理问题思路及解决办法
2016/02/13 Python
Python cookbook(数据结构与算法)保存最后N个元素的方法
2018/02/13 Python
利用Python代码实现数据可视化的5种方法详解
2018/03/25 Python
python 列表删除所有指定元素的方法
2018/04/19 Python
python dataframe常见操作方法:实现取行、列、切片、统计特征值
2018/06/09 Python
Sanic框架异常处理与中间件操作实例分析
2018/07/16 Python
python判断完全平方数的方法
2018/11/13 Python
Python爬虫设置代理IP(图文)
2018/12/23 Python
opencv实现简单人脸识别
2021/02/19 Python
python实现大学人员管理系统
2019/10/25 Python
Python 中如何实现参数化测试的方法示例
2019/12/10 Python
使用Pycharm分段执行代码
2020/04/15 Python
python如何调用百度识图api
2020/09/29 Python
大学应届毕业生个人求职信
2013/09/23 职场文书
标准毕业生自荐信范文
2013/11/04 职场文书
创业资金计划书
2014/02/06 职场文书
啤酒节策划方案
2014/05/28 职场文书
学校运动会报道稿
2014/09/23 职场文书
个人租房协议书
2014/11/28 职场文书
武夷山导游词
2015/02/03 职场文书
教师反邪教心得体会
2016/01/15 职场文书
心得体会格式及范文
2016/01/25 职场文书
AJAX学习笔记
2021/05/18 Javascript
各国货币符号大全
2022/02/17 杂记
Java中的Kafka为什么性能这么快及4大核心详析
2022/09/23 Java/Android