Python——Tchisla求解器(暴力搜索法)

Tchisla简介

最近玩到一个挺有意思的数字解密小游戏《Tchisla》,其规则类似算24点,也是利用一些数学运算和初始数字计算出目标数字,与算24点不同的是,Tchisla允许不限次数地使用一种初始数字(1~9),运算操作除了加减乘除外还包括了幂、平方根和阶乘,以及重复这个数字形成多位数(比如初始数字为7,那么777也可以使用)。游戏中的题目以“target # seed”的形式给出,我们的目标就是使用尽量少的初始数字seed加以各种计算得到目标数字target,比如题目11 # 7,最简解法就是11 = 77 / 7,比如题目5 # 9,最简解法为5 = √9 + (√9)! / √9。这里有篇文章更详细地介绍了这个游戏插电数字游戏——Tchisla 。
在这里插入图片描述
在这里插入图片描述

Tchisla求解器

这个游戏讲道理还是挺难的,尤其是挑战题目中的目标数都上千了,手动解题很难找到最优解,因此我花了半天用python写了个求解器,目前看起来效果不错,几十秒钟就帮我找到了了2016 # 1~9的所有最优解:
在这里插入图片描述

在求解Tchisla题目的过程中我相信是有一些内在的数学规律的,不过似乎很难总结出一套普适的最优求解算法,还是得暴力求解。暴力求解的思路其实挺简单的,从1个初始数字开始,找到所有合理的可达值及其表达式,放入一个列表中,这个称为第1代。后面再继续搜索第2代、第3代等等,对于第n代,其中的可达值就是n个初始数字可以表示的所有结果,计算第n代时我们可以根据前面的n-1代结果进行组合来生成新一代数据,比如5个初始数字可以得到的目标值就是第1代结果+第4代结果的各种算数组合加上第2代结果+第3代结果的各种算数组合。过程中一但发现目标值,我们就得到答案了。

虽然思路很简单,不过其中有些要注意的点,首先开方和阶乘运算这样的单目运算符不消耗初始数字,可以对一个表达式无限操作,为了避免无限搜索下去,所以第一点限制就是开方只对整数做、阶乘只对小于一定值的整数做。另外浮点数的精度丢失问题比较麻烦,需要合适的取整。此外,为了避免一些不太可能的无效搜索,合理的限制搜索数据的最大值也是必要的。

除了以上思路之外,为了得到结果,我们还需要一套表达式系统来跟踪我们的计算步骤,毕竟我们需要知道的最终是如何运算得到目标值而不仅仅是可以得到目标值的一个肯定。

以下放出代码,除了阶乘调用了系统库外,没有依赖别的库。代码前半部分是表达式系统,后半部分是求解器:

from math import factorialclass Expr:threshold = 1e-10def __init__(self, value):if isinstance(value, float):if value.is_integer():value = int(value)elif abs(value - round(value)) < Expr.threshold:value = round(value)self.value = valuedef __str__(self):raise TypeError("Should not call Expr.__str__()")class LiteralExpr(Expr):def __init__(self, value):Expr.__init__(self, value)self.literal = str(value)def __str__(self):return self.literalclass FactorialExpr(Expr):def __init__(self, expr: Expr):Expr.__init__(self, factorial(expr.value))self.child = exprdef __str__(self):if isinstance(self.child, BinaryExpr):return '({})!'.format(str(self.child))else:return str(self.child) + '!'class SqrtExpr(Expr):def __init__(self, expr: Expr):Expr.__init__(self, expr.value ** 0.5)self.child = exprdef __str__(self):if isinstance(self.child, LiteralExpr):return '√{}'.format(str(self.child))else:return '√({})'.format(str(self.child))class BinaryExpr(Expr):def __init__(self, oper: str, left: Expr, right: Expr, value):Expr.__init__(self, value)self.oper = operself.left = leftself.right = rightdef __str__(self):format_str = '({})' if isinstance(self.left, BinaryExpr) else '{}'format_str += ' {} 'format_str += '({})' if isinstance(self.right, BinaryExpr) else '{}'return format_str.format(str(self.left), self.oper, str(self.right))class AddExpr(BinaryExpr):def __init__(self, left: Expr, right: Expr):BinaryExpr.__init__(self, '+', left, right, left.value + right.value)class SubExpr(BinaryExpr):def __init__(self, left: Expr, right: Expr):BinaryExpr.__init__(self, '-', left, right, left.value - right.value)class MulExpr(BinaryExpr):def __init__(self, left: Expr, right: Expr):BinaryExpr.__init__(self, '*', left, right, left.value * right.value)class DivExpr(BinaryExpr):def __init__(self, left: Expr, right: Expr):BinaryExpr.__init__(self, '/', left, right, left.value / right.value)class PowExpr(BinaryExpr):def __init__(self, left: Expr, right: Expr):BinaryExpr.__init__(self, '^', left, right, left.value ** right.value)class TchislaSolver:value_limit = 10 ** 8power_limit = 30factorial_limit = 15class Found(BaseException):passdef __init__(self, target: int, seed: int):self.target = targetself.seed = seedself.all_candidates = set()self.current_candidates = []self.generations = []self.result = Nonedef solve(self, search_depth=10):try:while search_depth > 0:begin, end = 0, len(self.generations) - 1while begin <= end:self.cross_candidates(self.generations[begin], self.generations[end])begin += 1end -= 1self.add_literal(len(self.generations) + 1)self.next_generation()search_depth -= 1except TchislaSolver.Found:passreturn self.resultdef cross_candidates(self, c1, c2):for expr1 in c1:for expr2 in c2:self.add_addition(expr1, expr2)self.add_subtraction(expr1, expr2)self.add_multiplication(expr1, expr2)self.add_division(expr1, expr2)self.add_power(expr1, expr2)def add_candidate(self, expr):if expr.value == self.target:self.result = exprraise TchislaSolver.Found()if expr.value > TchislaSolver.value_limit:returnif expr.value not in self.all_candidates:self.all_candidates.add(expr.value)self.current_candidates.append(expr)self.add_factorial(expr)self.add_square_root(expr)def next_generation(self):self.generations.append(self.current_candidates)self.current_candidates = []def add_literal(self, repeats: int):self.add_candidate(LiteralExpr(int(str(self.seed) * repeats)))def add_addition(self, expr1: Expr, expr2: Expr):self.add_candidate(AddExpr(expr1, expr2))def add_subtraction(self, expr1: Expr, expr2: Expr):if expr1.value > expr2.value:self.add_candidate(SubExpr(expr1, expr2))else:self.add_candidate(SubExpr(expr2, expr1))def add_multiplication(self, expr1: Expr, expr2: Expr):self.add_candidate(MulExpr(expr1, expr2))def add_division(self, expr1: Expr, expr2: Expr):if expr1.value == 0 or expr2.value == 0:returnself.add_candidate(DivExpr(expr1, expr2))self.add_candidate(DivExpr(expr2, expr1))def add_power(self, expr1: Expr, expr2: Expr):if isinstance(expr2.value, int) and expr2.value <= TchislaSolver.power_limit:self.add_candidate(PowExpr(expr1, expr2))if isinstance(expr1.value, int) and expr1.value <= TchislaSolver.power_limit:self.add_candidate(PowExpr(expr2, expr1))def add_factorial(self, expr: Expr):if isinstance(expr.value, int) and expr.value <= TchislaSolver.factorial_limit:self.add_candidate(FactorialExpr(expr))def add_square_root(self, expr: Expr):if isinstance(expr.value, int) and expr.value > 0:self.add_candidate(SqrtExpr(expr))

测试

这个求解器用起来很简单,可以编写一个简单的例子进行测试,计算2016 # 1~9的最优解:

for i in range(1, 10):ts = TchislaSolver(2016, i)print('2016 = ' + str(ts.solve()))

结果如下:
在这里插入图片描述
代码中对搜索和运算是有一些限制的,默认搜索10代,可达值最大只接受10^8,幂指数最大30,阶乘数最大15,这些限制可以加快搜索速度,一般是够用的,如果没搜索到最优解,也可以尝试修改以下值来调整并重新搜索:

  value_limit = 10 ** 8power_limit = 30factorial_limit = 15

从上面的结果图里还可以发现表达式系统还有优化的空间,有些括号是多余的,不过懒得搞了,能用就行~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/714715.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【VSCODE修改代码行间距】解决方案

在我们编码的过程中&#xff0c;由于显示字体和显示器的不同&#xff0c;会需要调整行间距&#xff0c;在vscode默认的选项中没有看到设定行间距的选项&#xff0c;不过&#xff0c;可以手动修改配置档达到目的。 1.打开设置 2.打开配置档&#xff0c;手动进行设定 3.在选项中添…

第七十二天 漏洞发现-Web框架中间件联动GobyAfrogXrayAwvsVulmap

第72天 漏洞发现-Web框架中间件&联动&Goby&Afrog&Xray&Awvs&Vulmap 知识点&#xff1a; 1、Bup简单介绍&使用说明 2、Xray简单介绍&使用说明 3、AWWS简单介绍&使用说明 4、Goby简单介绍&使用说明 5、Afrog简单介绍&使用说明 6、…

带你玩转java封装和继承(上)

上次带大家学习了java里面比较重要的知识点类和对象&#xff0c;而且我们知道java是一门面向对象的语言&#xff0c;有时一个程序里可能有很多类&#xff0c;那么这么多类他们之间有什么联系吗&#xff1f;今天就带大家学习一下java类之间的关系。 什么是继承&#xff1a; 我们…

Linux信号【systemV】

目录 前言 正文&#xff1a; 1消息队列 1.1什么是消息队列&#xff1f; 1.2消息队列的数据结构 1.3消息队列的相关接口 1.3.1创建 1.3.2释放 1.3.3发送 1.3.4接收 1.4消息队列补充 2.信号量 2.1什么是信号量 2.2互斥相关概念 2.3信号量的数据结构 2.4…

蓝桥杯-单片机组基础5——外部中断与LED的控制(附小蜜蜂课程代码)

蓝桥杯单片机组备赛指南请查看这篇文章&#xff1a;戳此跳转蓝桥杯备赛指南文章 本文章针对蓝桥杯-单片机组比赛开发板所写&#xff0c;代码可直接在比赛开发板上使用。 型号&#xff1a;国信天长4T开发板&#xff08;绿板&#xff09;&#xff0c;芯片&#xff1a;IAP15F2K6…

自学软件测试怎么学?

软件测试是一个变得越来越受欢迎的行业&#xff0c;在IT行业里面&#xff0c;也是初学比较容易的。但对小白而言&#xff0c;怎样学习才能做到快速入门&#xff0c;少走弯路呢&#xff1f; 步骤一&#xff1a;初学学软件测试&#xff0c;要先搞懂这种问题 要想进入到软件测试…

Android Duplicate class 排除重复类

一、起因&#xff1a; 在迭代开发的时候&#xff0c;发现2个ijk很多类重复。但又2个库实现的功能是不一样&#xff0c;目前不能合并。但又想保留2个功能。需要排除其中一个库。 二、报错如何下图&#xff1a; 三、解决方法&#xff1a; 3.1 在terminal 也就是命令行处输入 …

在Windows 11中运行磁盘清理工具的9种方法,总有一种适合你

自Windows98以来,微软在操作系统中包含了一个内置的清理工具。当用户转向第三方清理应用程序时,这个值得信赖的实用程序站稳了脚跟。微软甚至宣布,第三方应用程序几乎毫无用处,删除注册表项不是一个好主意。 磁盘清理工具可以帮助删除临时文件、旧的更新日志、缩略图缓存和…

git安装与使用4.3

一、git的安装 1、下载git包 下载git包url&#xff1a;https://git-scm.com/download/win 下载包分为&#xff1a;64位和32位 2、点击安装包 2、选择安装路径 3、 点击下一步 4、点击next 5、点击next 6、点击next 7、 8、 9、 10、 11、 12、在桌面空白处&#xff0c;右键…

【SpringBoot】测试单元使用多线程

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;SpringBoot ⛺️稳重求进&#xff0c;晒太阳 问题产生 今天学习了乐观锁&#xff0c;但在测试单元执行多线程的时候出现了问题&#xff0c;多线程并没有直接结果 在控制台没有任何输出…

KubeSphere平台安装系列之二【Linux单节点部署KubeSphere】(2/3)

**《KubeSphere平台安装系列》** 【Kubernetes上安装KubeSphere&#xff08;亲测–实操完整版&#xff09;】&#xff08;1/3&#xff09; 【Linux单节点部署KubeSphere】&#xff08;2/3&#xff09; 【Linux多节点部署KubeSphere】&#xff08;3/3&#xff09; **《KubeS…

RocketMQ学习笔记一

课程来源&#xff1a;002-MQ简介_哔哩哔哩_bilibili &#xff08;尚硅谷老雷&#xff0c;时长19h&#xff09; 第1章 RocketMQ概述 1. MQ是什么&#xff1f; 2. MQ用途有哪些&#xff1f; 限流削峰&#xff1b;异步解耦&#xff1b;数据收集。 3. 常见MQ产品有哪些&对比…

Kaggle竞赛之Titanic存活预测2

提高代码规范性&#xff0c;基于上一个 baseline 的提高 import pandas as pd from sklearn.preprocessing import LabelBinarizer from sklearn.preprocessing import StandardScaler from sklearn.model_selection import train_test_split#数据划分方法 from sklearn.ensem…

哪个超声波清洗机品牌值得入手?销量榜品牌值得选购!

在科技日益发展的今天&#xff0c;超声波清洗技术以其高效、便捷和深度清洁的特点&#xff0c;已经深入到生活的诸多领域&#xff0c;从精密仪器到珠宝首饰&#xff0c;从眼镜框到假牙&#xff0c;甚至是厨房用品的日常护理&#xff0c;都能见到超声波清洗机的身影。面对市场上…

免杀实战-EDR对抗

文章目录 杀软分析BOF.NET 杀软分析 x64dgb简单调试发现该edr在r3环对ntdll.dll和kernel32.dll关键函数均存在hook&#xff0c;这里硬盘读取原来的dll进行重新加载&#xff0c;原理如图 loader // dllmain.cpp : 定义 DLL 应用程序的入口点。 #include "pch.h" #in…

DSI2协议之BTA行为理解

概念: DSI协议spec支持总线控制权在master和slave之间发生交换,即通过bus turn around来实现; BUS TURN AROUND: BTA 的实现是通过controller—>cdphy的turnrequest信号来实现; 关于控制器发出turnrequest给phy,phy通过lvds/trio线输出turnaround sequence如下图中…

BUGKU 本地管理员

打开环境&#xff0c;先F12查看看到一串代码。Base64解码一下&#xff0c;得到的应该是密码&#xff0c;然后输入admin | test123试一下 使用BP抓包&#xff0c;修改XFF&#xff0c;得到flag

将镜像上传到私有镜像仓库Harbor

首先你需要安装Harbor服务&#xff1a; https://blog.csdn.net/qq_50247813/article/details/136388229 客户端已经安装docker&#xff1a; https://docs.docker.com/engine/install/centos/ 在docker客户端登录 Harbor 我的Harbor 服务器地址&#xff1a; 192.168.44.161 账号…

关于编写测试用例的一些思考

测试用例是QA同学的基本功&#xff0c;每个人都有一套编写测试用例的体系&#xff0c;本文是作者结合自身的工作经验以及阅读一些测试相关的书籍后的一些看法&#xff0c;欢迎大家一起讨论学习。 测试设计 测试用例格式 面试中一些常见的问题 1.APP测试与服务端测试的区别&am…

何恺明新作 l-DAE:解构扩散模型

何恺明新作 l-DAE&#xff1a;解构扩散模型 提出背景扩散模型步骤如何在不影响数据表征能力的同时简化模型&#xff1f;如何进一步推动模型向经典DAE靠拢&#xff1f;如何去除对生成任务设计的DDM中不适用于自监督学习的部分&#xff1f;如何改进DDM以专注于清晰图像表示的学习…