[Python] PTA 用扑克牌计算24点 Python解法 itertools permutations函数的使用 全排列实例

一副扑克牌的每张牌表示一个数(J、Q、K 分别表示 11、12、13,两个司令都表示 6)。任取4 张牌,即得到 4 个 1~13 的数,请添加运算符(规定为加+ 减- 乘* 除/ 四种)使之成为一个运算式。每个数只能参与一次运算,4 个数顺序可以任意组合,4 个运算符任意取 3 个且可以重复取。运算遵从一定优先级别,可加括号控制,最终使运算结果为 24。请输出一种解决方案的表达式,用括号表示运算优先。如果没有一种解决方案,则输出 -1 表示无解。

输入格式:

输入在一行中给出 4 个整数,每个整数取值在 [1, 13]。

输出格式:

输出任一种解决方案的表达式,用括号表示运算优先。如果没有解决方案,请输出 -1。

输入样例:

2 3 12 12

输出样例:

((3-2)*12)+12

 重要函数:

首先我们了解一下python中的permutations函数,这是一个全排列函数,可以将输入的列表中的各个元素全排列并输出:

演示

from itertools import permutations
in_list = [1, 2, 3]
print(list(permutations(in_list)))

输出结果

[(1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1), (3, 1, 2), (3, 2, 1)]

可以见到当我们输入一组数时,该函数可以将数据迅速全排列

思路

计算

我们需要一个函数来帮我们应对不同的+-*/运算,于是我定义了一个函数

def clc_with_symbol(num_a, clc_symbol, num_b):
    # 符号计算器,需要 数字num_a 符号clc_symbol 数字num_b,返回浮点数
    num_a = float(num_a)
    num_b = float(num_b)
    if clc_symbol == '+':
        return num_a + num_b
    elif clc_symbol == '-':
        return num_a - num_b
    elif clc_symbol == '*':
        return num_a * num_b
    elif clc_symbol == '/':
        if num_b == 0:
            return 99999
        return num_a / num_b

有了这个函数,我们就可以使用(数字)(符号)(数字)的格式来计算当前式子的结果,美中不足的是我还没有很好的应对除数是0得到情况

运算顺序

接着,我们需要处理运算顺序和符号顺序

我们稍加思索可以的出,四个数字的计算顺序总共就5种,分别有

((x # x) # x) # x
(x # x) # (x # x)
(x # (x # x)) # x
x # ((x # x) # x)
x # (x # (x # x))

(x代表数字,#代表符号)

这个思路我使用的是排列符号和全排列数组的交叉计算

我们先利用全排列的数组的[0],[1],[2],[3]位分别放到式子的1234位,然后让排列好的符号分别填充到每个数字之间,计算是否等于24然后输出

符号排列代码:

symbol_s = ['+', '-', '*', '/']  # 符号列队,方便下面排列符号
symbol_list = []
for i in range(4):
    for k in range(4):
        for m in range(4):
            symbol_list.append([symbol_s[i], symbol_s[k], symbol_s[m]])

其中一种计算的代码是

    if clc_with_symbol(clc_with_symbol(clc_with_symbol(perm[0],symbol[0],
                perm[1]),symbol[1], perm[2]), symbol[2], perm[3]) == 24:  
                                                       # ((x # x) # x) # x
        print(f'(({perm[0]}{symbol[0]}{perm[1]}){symbol[1]}
                {perm[2]}){symbol[2]}{perm[3]}')

PS代码较长,显示不完整,以下有截屏

这段代码计算的是((x # x) # x) # x运算顺序的式子,symbol是符号组里面的一个子符号组,perm是全排列组里面的一个组

以下是全部代码

代码

from itertools import permutations
def clc_with_symbol(num_a, clc_symbol, num_b):
    # 符号计算器,需要 数字num_a 符号clc_symbol 数字num_b,返回浮点数
    num_a = float(num_a)
    num_b = float(num_b)
    if clc_symbol == '+':
        return num_a + num_b
    elif clc_symbol == '-':
        return num_a - num_b
    elif clc_symbol == '*':
        return num_a * num_b
    elif clc_symbol == '/':
        if num_b == 0:
            return 99999
        return num_a / num_b


module = 1  # 锚点,module值为0表示输出所有可能的排列,module值为1表示输出一次
in_list = list(input().split())
permuted_list = list(permutations(in_list))
symbol_s = ['+', '-', '*', '/']  # 符号列队,方便下面排列符号
symbol_list = []
for i in range(4):
    for k in range(4):
        for m in range(4):
            symbol_list.append([symbol_s[i], symbol_s[k], symbol_s[m]])
flag = 0  # 插眼
for perm in permuted_list:
    for symbol in symbol_list:
        if clc_with_symbol(clc_with_symbol(clc_with_symbol(perm[0], symbol[0], perm[1]), symbol[1], perm[2]), symbol[2], perm[3]) == 24:  # ((x # x) # x) # x
            print(f'(({perm[0]}{symbol[0]}{perm[1]}){symbol[1]}{perm[2]}){symbol[2]}{perm[3]}')
            flag = 1
            if module:
                break
        elif clc_with_symbol(clc_with_symbol(perm[0], symbol[0], perm[1]), symbol[1], clc_with_symbol(perm[2], symbol[2], perm[3])) == 24:  # (x # x) # (x # x)
            print(f'({perm[0]}{symbol[0]}{perm[1]}){symbol[1]}({perm[2]}{symbol[2]}{perm[3]})')
            flag = 1
            if module:
                break
        elif clc_with_symbol(clc_with_symbol(perm[0], symbol[0], clc_with_symbol(perm[1], symbol[1], perm[2])), symbol[2], perm[3]) == 24:  # (x # (x # x)) # x
            print(f'({perm[0]}{symbol[0]}({perm[1]}{symbol[1]}{perm[2]})){symbol[2]}{perm[3]}')
            flag = 1
            if module:
                break
        elif clc_with_symbol(perm[0], symbol[0], clc_with_symbol(clc_with_symbol(perm[1], symbol[1], perm[2]), symbol[2], perm[3])) == 24:  # x # ((x # x) # x)
            print(f'{perm[0]}{symbol[0]}(({perm[1]}{symbol[1]}{perm[2]}){symbol[2]}{perm[3]})')
            flag = 1
            if module:
                break
        elif clc_with_symbol(perm[0], symbol[0], clc_with_symbol(perm[1], symbol[1], clc_with_symbol(perm[2], symbol[2], perm[3]))) == 24:  # x # (x # (x # x))
            print(f'{perm[0]}{symbol[0]}({perm[1]}{symbol[1]}({perm[2]}{symbol[2]}{perm[3]}))')
            flag = 1
            if module:
                break
    if flag == 1:
        if module:
            break
if flag == 0:
    print("-1")

假设我在锚点module设置为0的情况下(输出所有可能),输入 2 3 12 12,结果为:

2 3 12 12
((3-2)*12)+12
((3-2)*12)+12
((3*12)+12)/2
((3*12)+12)/2
12-((2-3)*12)
(12/(2*3))*12
**内容过长已折叠**
(12+12)*(3-2)
(12+(12*3))/2
(12+12)/(3-2)
12*((12/3)-2)
(12*12)/(3*2)
((12*12)/3)/2

进程已结束,退出代码0

可以看到计算结果都是正确的

总结

感谢permutations,让我免去了递归的麻烦

  • 17
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值