如何扩展itertools组合可能性以解决四则运算表达式匹配问题
解决4个6生成目标表达式的问题:突破64种组合限制
你遇到的问题根本不是itertools的限制,而是只生成了无括号的线性表达式——用itertools.product(['+','-','*','/'], repeat=3)只能得到6 op1 6 op2 6 op3 6这种固定顺序的结构,共4^3=64种,但括号带来的运算顺序变化完全没覆盖,比如(6-(6/6))*6这类表达式属于不同的运算结构,需要用递归分治的方式生成所有可能的表达式。
核心思路:递归生成所有表达式结构
括号的本质是改变运算的结合顺序,我们可以把n个数字的表达式拆分为左右两个子表达式的组合,递归生成所有可能的子表达式,再用运算符拼接。以4个6为例:
- 拆分为「1个6」和「3个6的表达式」,再用运算符组合
- 拆分为「2个6的表达式」和「2个6的表达式」,再用运算符组合
- 拆分为「3个6的表达式」和「1个6」,再用运算符组合
每个子表达式又可以继续拆分,直到只剩单个数字,这样就能覆盖所有带括号的情况。
具体实现代码
def generate_expressions(nums): # 递归终止条件:单个数字 if len(nums) == 1: num = nums[0] return [(str(num), num)] expressions = [] # 遍历所有拆分点,把nums分成左右两部分 for k in range(1, len(nums)): left_nums = nums[:k] right_nums = nums[k:] # 递归生成左右两边的所有表达式和对应值 left_exprs = generate_expressions(left_nums) right_exprs = generate_expressions(right_nums) # 遍历所有运算符和左右表达式的组合 for op in ['+', '-', '*', '/']: for (expr_left, val_left) in left_exprs: for (expr_right, val_right) in right_exprs: # 处理除法除零的情况 if op == '/' and val_right == 0: continue # 组合新表达式 new_expr = f"({expr_left}{op}{expr_right})" # 计算新值(注意浮点数精度,这里保留原始计算) try: new_val = eval(f"{val_left}{op}{val_right}") except ZeroDivisionError: continue expressions.append((new_expr, new_val)) return expressions # 生成4个6的所有表达式 all_exprs = generate_expressions([6,6,6,6]) # 查找目标值30的表达式 target = 30 matching_exprs = [expr for expr, val in all_exprs if abs(val - target) < 1e-9] for expr in matching_exprs: print(expr)
代码说明
- 递归拆分:通过拆分数字列表,生成所有可能的子表达式组合,覆盖了所有括号带来的运算顺序变化
- 运算符遍历:遍历所有运算符并结合递归结构,不再局限于线性排列的表达式
- 细节处理:跳过除法除零的情况,用浮点数精度判断避免因计算误差漏掉目标值
运行这段代码后,你会找到((6-(6/6))*6)这类符合要求的表达式,生成的组合数远不止64种。
内容的提问来源于stack exchange,提问作者Lucas
相关产品推荐
相关产品推荐

