問題描述:
給定四個整數,任意使用加、減、乘、除和小括號,構造出一個表達式,使得其最終結果為24,并輸出這四個數字所組成的所有表達式。
例如:
34-15/3-5=24
代碼
'''
combinations:獲取列表任意長度的子集
'''
from itertools import combinations
import re
from numpy import random
class Solver:
# 需要達成的目標值
target = 24
# 四則運算符
ops = ['+', '-', '*', '/', '--', '//']
# precise_mode為精準模式,若開啟,則減號及除號后開啟括號
def __init__(self, precise_mode=False):
self.precise_mode = precise_mode
def solution(self, nums):
result = []
groups = self.dimensionality_reduction(self.format(nums))
for group in groups:
for op in self.ops:
exp = self.assemble(group[0], group[1], op)['exp']
if self.check(exp, self.target) and exp not in result:
result.Append(exp)
return [exp + '=' + str(self.target) for exp in result]
# 對需要處理的數字或表達式組合進行降維,降到二維
def dimensionality_reduction(self, nums):
result = []
# 如果維數大于2,則選出兩個表達式組合成一個,從而降低一個維度,通過遞歸降低到二維
if len(nums) > 2:
for group in self.group(nums, 2):
for op in self.ops:
new_group = [self.assemble(group[0][0], group[0][1], op)] + group[1]
result += self.dimensionality_reduction(new_group)
else:
result = [nums]
return result
# 將兩個表達式組合成一個新表達式
def assemble(self, exp1, exp2, op):
# 如果運算符為'--'或者'//',則交換數字順序重新計算
if op == '--' or op == '//':
return self.assemble(exp2, exp1, op[0])
if op in r'*/':
exp1 = self.add_parenthesis(exp1)
exp2 = self.add_parenthesis(exp2)
if self.precise_mode:
if op == '-':
exp2 = self.add_parenthesis(exp2)
elif op == '/':
exp2 = self.add_parenthesis(exp2, True)
exp = self.convert(exp1['exp'] + op + exp2['exp'], op)
return {'op': op, 'exp': exp}
# 根據需要為表達式添加相應的括號
@staticmethod
def add_parenthesis(exp, is_necessary=False):
# 如果上一步計算步驟的運算符號為加號或減號,則需要添加括號
if (is_necessary and not exp['exp'].isdigit()) or exp['op'] in r'+-':
result = {
'exp': '(' + exp['exp'] + ')',
'op': exp['op']
}
else:
result = exp
return result
# 檢查表達式是否與結果相等,考慮到中間步驟的除法,因此不采用相等判斷,而是采用計算值和目標值的絕對值是否符合某個精度
@staticmethod
def check(exp, target, precision=0.0001):
try:
return abs(eval(exp) - target) < precision
except ZeroDivisionError:
return False
# 將表達式各項重新排序成為等價標準表達式
@staticmethod
def convert(exp, op):
if op in r'+-':
pattern = r'([+-](((.+)|d+)[*/]((.+)|d+)|d+))'
exp = '+' + exp
else:
pattern = r'([*/]((.+?)|d+))'
exp = '*' + exp
result = ''.join(sorted([i[0] for i in re.findall(pattern, exp)]))
if len(result) != len(exp):
result = exp
return result[1:]
# 將輸入的數字格式化為字典,數字的運算符為空格
@staticmethod
def format(nums):
return [{'op': ' ', 'exp': str(num)} for num in nums]
# 對表達式列表進行分組,返回列表
@staticmethod
def group(exp_list, counter):
# 生成以下標號為元素的列表
index_list = [i for i in range(len(exp_list))]
# 以下標號列表取出不重復的組合
combination = list(combinations(index_list, counter))
# 使用下標得到原表達式組成最終的結果數組
for group1 in combination:
group2 = list(set(index_list) - set(group1))
yield [
[exp_list[g1] for g1 in group1],
[exp_list[g2] for g2 in group2]
]
auto_input = False
if auto_input:
customer_input = random.randint(1, 20, size=4)
else:
customer_input = list()
customer_input.append(input("請輸入第一個數字:"))
customer_input.append(input("請輸入第二個數字:"))
customer_input.append(input("請輸入第三個數字:"))
customer_input.append(input("請輸入第四個數字:"))
task = Solver()
answer = task.solution(customer_input)
if len(answer) == 0:
print('No solutions')
else:
for a in answer:
print(a)
運行結果
請輸入第一個數字:1
請輸入第二個數字:2
請輸入第三個數字:6
請輸入第四個數字:4
(2-1)*4*6=24
4*6/(2-1)=24
(2+6)*(4-1)=24