|
15天能写出这种级别代码,很棒啦
目前:穷举所有排列×运算×括号模板冗余大、重复多~
可以不改,要是改为“递归合并两数”的24点标准解法:
每次从集合里选两数做运算,放回结果,直到只剩一个数是否为24。
天然覆盖所有括号形态,避免模板爆炸;
同时通过只对有序/无序对做必要运算来剪枝(如对+、*的交换律去重)。
参考:
- import easygui as eg
- from fractions import Fraction
- from functools import lru_cache
- import itertools
- def ask_numbers():
- while True:
- fields = ['第一个数','第二个数','第三个数','第四个数']
- resp = eg.multenterbox(msg='请输入四个1~10的正整数', title='输入框', fields=fields)
- if resp is None:
- return None # 用户取消
- try:
- nums = [int(x.strip()) for x in resp]
- if len(nums) != 4 or any(n < 1 or n > 10 for n in nums):
- eg.msgbox('必须是4个1~10的正整数。', '输入有误')
- continue
- return nums
- except:
- eg.msgbox('请输入有效的整数。', '输入有误')
- def format_expr(expr: str) -> str:
- # 去除形如"24/1"这类无意义格式不在此做,保持表达式原意;无需去 .0(我们不用浮点)
- return expr
- def enumerate_results(nums, find_all=True):
- # 将int转为Fraction并同时携带表达式字符串
- start = tuple( (Fraction(n), str(n)) for n in nums )
- @lru_cache(None)
- def solve(state):
- # state: tuple of (Fraction, expr_str) 的序列,但cache里不能存对象,存值和串
- pairs = list(zip(state[::2], state[1::2])) # 无用,只为类型提示;下面直接用索引
- results = set() # expr strings that evaluate to 24
- L = len(state)//2 # 我们把state编码成(value, expr)交错扁平化存放
- # 还原为列表[(val, expr), ...]
- arr = [(state[2*i], state[2*i+1]) for i in range(L)]
- if L == 1:
- val, expr = arr[0]
- if val == Fraction(24, 1):
- results.add(expr)
- return tuple(sorted(results)) # cache要求可哈希
- # 选择两数 i<j
- for i in range(L):
- for j in range(i+1, L):
- (a, ea) = arr[i]
- (b, eb) = arr[j]
- # 其余元素
- rest = [arr[k] for k in range(L) if k != i and k != j]
- candidates = []
- # 加法与乘法(交换律:只做一遍)
- candidates.append( (a+b, f"({ea}+{eb})") )
- candidates.append( (a*b, f"({ea}*{eb})") )
- # 减法和除法(考虑顺序)
- candidates.append( (a-b, f"({ea}-{eb})") )
- candidates.append( (b-a, f"({eb}-{ea})") )
- if b != 0:
- candidates.append( (a/b, f"({ea}/{eb})") )
- if a != 0:
- candidates.append( (b/a, f"({eb}/{ea})") )
- # 递归
- for (val, ex) in candidates:
- new_arr = rest + [(val, ex)]
- # 扁平化为 cache key
- new_state = []
- # 为提升剪枝效果,对值表达式做轻微排序:仅对交换律运算前已控制,不再额外排序表达式,排序按值大小
- for (v, e) in sorted(new_arr, key=lambda x: (x[0], len(x[1]))):
- new_state.extend([v, e])
- new_state = tuple(new_state)
- sub = solve(new_state)
- if sub:
- results.update(sub)
- if not find_all and results:
- return tuple(sorted(results))
- return tuple(sorted(results))
- # 枚举输入的所有排列(避免相同数字导致重复,用set去重)
- all_exprs = set()
- for perm in set(itertools.permutations(start, 4)):
- # 扁平化为 (v1,e1,v2,e2,...) 作为初态
- init = []
- for (v, e) in perm:
- init.extend([v, e])
- sols = solve(tuple(init))
- all_exprs.update(sols)
- if not find_all and all_exprs:
- break
- # 美化与去重
- clean = sorted(set(format_expr(s) for s in all_exprs))
- return clean
- def main():
- nums = ask_numbers()
- if nums is None:
- return
- # 询问模式:找到一个就停 or 列出全部
- choice = eg.buttonbox("选择求解模式:", "24点", choices=["快速找到一个", "列出全部解"])
- find_all = (choice == "列出全部解")
- sols = enumerate_results(nums, find_all=find_all)
- if not sols:
- eg.msgbox('无解', '24点')
- else:
- if find_all:
- # 多解时分行显示
- eg.textbox("找到的解:", "24点", "\n".join(sols))
- else:
- eg.msgbox(f"结果:{sols[0]}", '24点')
- if __name__ == "__main__":
- main()
复制代码 |
|