概述
公司目标产品定义需求,需要生成应用题讲解步骤。而经大量的题型总结,K12应用题可以抽象为纯数学问题:
- 多轮一元一次方程求解(自定义名称):可通过知n求1的形式逐步求解
如:已知宽高,求矩形面积- 二元一次方程求解:可通过列举二元一次方程式求解答案
如已知x + y = n, x - y = m, 求 x 或 y- 带条件的二元一次方程求解:
如方案类问题,已知 x + y = n, 当x * y 取最大值时,x 和 y 的取值是多少
而对于K12数学问题而言,通过知n求1的形式逐步求解的题目占比很高,且对于符合多轮一元一次方程求解的应用题,其讲解顺序与求解顺序一致,因此抽象为一类通用的数学模型求解意义重大。
由于从应用题题目的字符串 到 应用题的求解步骤 基于原项目的整体设计,经过了多个步骤处理,内容相对较多。此处仅介绍求解逻辑,即“多轮一元一次方程求解”数学模型的设计与实现
模型设计
类图设计
其中红色框内是抽象的数学模型;蓝色框内为可扩展的场景内容 和 数学关系式集(数学描述方式)。
数学模型抽象
主要类对象
类参数公式:可重复使用的存储业务对象类为参数的公式类,以业务对象方法匹配参数
公式求解集:为了降低倒推公式的复杂性,保存公式各个参数倒推式对象
关系式 : 根据题目解析的数学关系,以名称匹配参数
求解式:单纯的公式NLG输出类
参数:用以匹配对象、公式
class 参数 {
static 子类名称集 = {
include: [],
exclude: []
}
static test(str) {
if (this.constructor === 参数) return false
return this.子类名称集.include.some(val => {
if (isRegExp(val)) return val.test(str)
else if (isString(val)) return val === str
else throw Error(`Include 参数只支持 RegExp 和 String 类型`)
}) && !this.子类名称集.exclude.includes(str)
}
// ...
}
主要算法
- 采用类深度搜索获取求解式的算法
// 多轮一元一次方程求解问题.js
function 获取解答求解式数组() {
while (求解对象集 不为空) {
// 1. 根据已知对象集,选择匹配的公式求解
for (let 类参数公式 of this.类参数公式集) {
if (类参数公式可应用于 已知参数 求解) {
// 1.1 获取已知参数 和 待求参数的类
// 1.2 从关联参数(公式、题目关系)中获取对象名称
// 1.3 处理 获取已知参数 的限定条件,推出待求参数的限定
// 1.4 如果不是求解对象集参数,重新生成已知参数
// 如果是求解对象集参数,从求解对象集获取参数
// 1.5 类参数公式 记录求解参数(避免再次应用公式)
// 1.6 生成并记录求解式
}
}
if (不可求解) {
// 2. 根据题目私有关系求解
for (let 私有关系式 of this.题目解析关系式集) {
// 类似 1.,减少了1.2不必要操作
}
}
}
}
2. 生成求解式集算法
// 多轮一元一次方程求解问题.js
// 根据求解式的求解项、参数项 倒推所有 有效的求解式 (待优化)
function 按求解集生成求解式集(求解式数组) {
let res = []
// 1. 可能有一题多问, 遍历求解集
for (let 求解对象 of this.求解集) {
// 2. 根据求解对象的已知参数倒推未知数
let 未知对象数组 = [求解对象]
while(未知对象数组.length > 0) {
// 2.1 如果是 解题前的已知参数 ,跳过
// 如果是 解题过程中产生的参数,保存至 未知对象数组,并保存该 求解式
// 2.2 移除 该遍历的第一个 求解对象
}
}
}
3.根据参数实例匹配类参数公式算法
// 公式Handler.js
static 可应用类参数(类参数公式, 参数实例) {
//(1)根据类参数公式,对参数实例进行分类
// (2) 根据分类参数判断类参数公式是否可用(待优化)
// 1. 如果存在多于一个类型的参数实例不存在,则不能应用该公式
// 2. 排列组合选择 已知参数;如果无法选择足够的已知参数,则该公式不可用
}
输入流程设计
为弥补OCR识别准确率的不足,产品增加表单输入流程
通过输入场景信息,判定特定场景,并根据场景属性(动词),生成对应的数学关系式,用以解析关系式(应用RegExp)
function 生成场景数学关系(场景) {
return 数学关系数组.map(数学关系 => {...})
}
表单转换说明
一元一次方程求解中,都可以按特定规则(自定义正则)对参数进行数学模型转换:
- 类参数公式为已知对象之间的关系,因此可以直接以对象名称进行命名/匹配:
[
'应纳税额 = 计税金额 * 适用税率',
'计税金额 = 应纳税额 / 适用税率',
'适用税率 = 应纳税额 / 计税金额'
]
- 题目解析关系式 可用文字解析为特定的数学关系,举例
消费税大约占售价的25%" => "消费税 = 售价 * 25%"
关系式可能与场景相关联,因此进行转换时需要通过特定的场景重新生成正则。通过正则表达式将文字输入转换为数学模型输入
// 目前暂时以Object类型表示
const 数学关系数组 = [
{
name: '含数字比例关系',
// 形如 "/^(?<目标对象>.+?)等于(?<被乘数>.+?)乘以?(?<乘数>\D+)$/" 的正则捕获式
regs: [],
// 将捕获对象生成为关系式、字符串参数数组、对象字符串数组
transform: function({目标对象, 被乘数, 乘数}){
return {
}
}
}
]
3. 基本类型的数值关系 表示格式为:[限定 +] 对象 + 数值 + 单位,举例
"11月份的计税金额20000元" =>
{
限定: ["11月份"],
对象: "计税金额",
数量: "2000",
单位: "元"
}
4. 求解集以自定义(如"多少")为标识区分名称和单位,格式为 [限定 +] 对象 + ”“ + 单位, 举例
"12月份的消费税多少元" =>
{
限定: ["12月份"],
对象: "消费税",
单位: "元"
}
表单输入示例
const 多轮一元一次方程求解问题参数定义 = {
/*
* @param {String} 扩展其他求解类型的标志位
* */
类型: "多轮一元一次方程求解问题",
/*
* @param {String} 单独提取场景说明,用于动态提供特定场景的规范表达名称
* */
场景名称: "纳税场景",
/*
* 场景是正则捕获的场景相关内容, 因场景输入而异, 输入以对象为item的数组,目前用来做答题的说明
* @param {Array<Object>} 场景正则捕获的主谓宾 + 匹配的场景
* Object:
* @param {String} 类型 场景名称
* @param {String} 主体
* @param {String} 动作
* @param {String} 客体
* 示例:
* TODO: 需要根据教案区分问题解答的主客体, 如"小明的饭店"中小明是需要在教案中展示的混淆主体(定语)
* */
场景: {[名称: "纳税场景",] 主体: "饭店", 动作: "缴纳", 客体: "营业税"},
/*
* 类参数公式是特定场景业务类的关系表示, 因业务场景而提前设定
* @param {Array<Array<String>>} 可提供多个求解公式,要求各公式各参数完整的求解
* 输入格式:
* 示例:
* [
* [
* '应纳税额 = 计税金额 * 适用税率',
* '计税金额 = 应纳税额 / 适用税率',
* '适用税率 = 应纳税额 / 计税金额'
* ]
* ]
* */
类参数公式集: [],
/*
* 固定格式的公式求解式
* @param {Array<String>} 规定格式的关系表达,需要定义各表达式的转换方式
* 示例:箭头前为原始输入,需经过关系正则匹配并转换,箭头后为转换后格式
* [
* "消费税大约占售价的25%", => 比例关系正则 => "消费税 = 售价 * 25%"
* ]
* */
题目解析关系式集: ["消费税大约占售价的25%"],
/*
* TODO: 需要重新根据更多用例完善定义参数对象的数据结构
* 如增加约束属性,区分"A的营业额" 和 "B的营业额", "10月的营业额"和"11月的营业额"
* @param {Array<Object>} 参数对象数组
* Object:
* @param <String> 类型: 是特定场景下的业务类对象则为该类名称, 否则为“参数”
* @param <String> 对象: 参数名称
* @param <Number> 数值: 参数的数量
* @param <String> 单位: 参数的单位
* @param <String> uid: 唯一标识,暂时是约束的替代品,如多个“参数”类型情况下的区分的标识
* 输入格式: [限定 + '的' +] 对象名称 + ['是'+ ] 数量 + 单位
* */
参数集: ["计税金额20000元"],
/*
* @param {Array<Object>} 同参数集对象定义,目前唯一区分是在“多轮一元一次方程求解问题”或其子类实例的不同属性中
* 输入格式: [限定 + '的' +] 对象名称 + "多少" + 单位
* */
求解集: ["消费税多少元"]
}
方案优缺点
扩展性
-
数学模型 和 业务场景 完全解耦
多轮一元一次方程的整体设计将数学模型 和 业务场景完全解耦,因此可以应用通过编写特定场景的业务对象类、教案生成逻辑应用至不同场景
-
数学关系抽象
抽象了数学关系类,可根据题目条件进行不同数学关系的匹配扩展
(1)如数学公式扩展
应纳税额 = 计税金额 * 适用税率 应纳税额 = 计税金额 - 税后余额(2)如数学关系扩展
A是B的5% => A = B * 5% A比B多10% => A = B * ( 1 + 10% ) -
业务对象增加限定属性(“约束”名称已在项目中存在),兼容多轮正则匹配
经调研,中文表示的方式虽然多种多样,但是K9以内的数学应用题表达格式遵循基本的语法结构:
因此,可以应用多轮正则 和 词语分类 进行对象转换,(多层次多轮正则匹配)初步设计如下:
1. 先匹配最短字段, 如 名词 + 名词,数量 + 名词
2. 后匹配形容词字段, 如 形容词 + 的 + 名词
3. 关系短语匹配,如A是B的5%,A占B的5%,按A的5%缴纳B(xx税)
4. 最后是自定义匹配, 如 关系短语匹配
待改进
- 算法改进:方法“可应用类参数”在“所有参数类型的参数实例都存在”的情况下,采用暴力求解
- 根据题意求解关系式 目前仅支持简单四则运算(一元一次的倒推式算法简答),目前不支持 多参数的 求解,例如:比例增量关系,a * (1 + 0.5) = b(已应用解决方案一解决)
解决方案:
- 应用类公式方法人为先写好倒推公式,而后进行参数映射、参数替换求解(目前实现方案)
- 写倒推算法:难