Python实现24点游戏:全排列与表达式生成详解
本文详解如何使用Python实现24点游戏。通过itertools模块进行数字全排列与运算符组合,利用zip_longest和chain拼接表达式,并结合eval与math.isclose处理浮点精度。文章涵盖基础排列、运算符组合及括号优先级处理,提供完整代码逻辑与测试案例。
24点游戏核心逻辑
24点游戏的规则是给定四个0到9之间的数字,通过加减乘除四则运算得出结果24。实现这一目标的核心思路是对这四个数字进行全排列,并在数字之间插入三个运算符,遍历所有可能的操作符组合,最终拼接成完整的数学表达式进行计算。
环境准备与依赖模块
演示环境为Windows 10,使用Python 3.7版本及pycharm 2018.2编辑器。实现过程主要依赖以下标准库模块:
math:用于处理浮点数精度判断。itertools:提供全排列和笛卡尔积生成器。collections.abc:用于处理迭代器相关操作。
数字全排列实现
首先,我们需要生成四个数字的所有排列顺序。Python的itertools.permutations函数可以接收一个可迭代对象,并返回其全排列的生成器。若指定第二个参数,则控制选取的数量;若不传,则默认取序列长度。
from itertools import permutations
data_list = permutations([1,2,3,4],2)
for data in data_list:
print(data)上述代码的输出结果展示了从[1, 2, 3, 4]中选取2个数字的所有排列情况:
(1, 2)
(1, 3)
(1, 4)
(2, 1)
(2, 3)
(2, 4)
(3, 1)
(3, 2)
(3, 4)
(4, 1)
(4, 2)
(4, 3)封装获取全排列的函数如下:
def get_all_data_sequence(data_iter):
return permutations(data_iter)运算符组合与表达式拼接
在确定数字顺序后,需要在数字之间插入运算符。加减乘除四种运算符(['+', '-', '*', '/'])在四个数字间需要插入三个。我们可以使用itertools.product来获取运算符的笛卡尔积。
product函数返回一个生成器,通过repeat参数指定序列重复的次数。例如,获取三个运算符的所有组合:
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)封装获取运算符序列的函数:
def get_all_operations_sequence():
operations = ['+','-','*','/']
return product(operations,repeat=3)接下来,将数字序列与运算符序列进行拼接。使用zip_longest以最长序列为基准进行配对,缺失值使用fillvalue参数填充(此处为空字符串)。随后,利用chain.from_iterable将嵌套的元组展平为单个迭代器,依次取出值进行字符串拼接。
data = zip_longest([1,2,3,4],['*','-','+'],fillvalue='')
for value in data:
print(value)拼接前的元组形式:
(1, '*')
(2, '-')
(3, '+')
(4, '')展平后的迭代处理:
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核心计算逻辑与浮点处理
在遍历所有数字排列和运算符组合后,我们需要构建表达式并计算结果。使用eval函数将拼接好的字符串作为代码执行,返回计算结果。由于浮点数运算存在精度问题,直接使用==判断是否等于24可能不准确,因此引入math.isclose进行近似相等判断。
此外,需处理除数为零的情况,通过try-except块捕获ZeroDivisionError并跳过该次循环。
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括号优先级的处理
上述基础逻辑仅能处理无括号的线性表达式。然而,24点游戏中括号可以改变运算优先级,例如8/(3-8/3)即可得出24。四个数字的运算中,括号最多有三个。我们需要罗列所有可能的括号组合情况,共计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))',
# 三个括号是重复的,就不用罗列出来了
]在实际实现中,需遍历这11种格式,将数字和运算符填入对应位置,生成带括号的表达式字符串,再次使用eval计算并判断结果。若找到结果为24的表达式,则返回该表达式及结果;若遍历完所有组合仍未找到,则返回None。
测试案例与结果验证
通过上述逻辑,我们可以验证不同的数字组合:
- 测试1, 2, 3, 4:系统可快速找到
1*2*3*4等解法。 - 测试8, 8, 3, 3:直接线性运算无法得到24,但通过括号组合
8/(3-8/3)即可成功运算出24。
该实现方式全面覆盖了数字排列、运算符组合及括号优先级,能够高效解决24点游戏问题。
以上就是Python秒算24点实现及原理详解的详细内容,更多关于Python算法与itertools应用资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









