使用java去对比2个带数学公式的字符串

首先大家看到这个题目,可能会不屑一顾,呵呵,是的,起初我也认为这是个很简单的任务,当任务拿到手里后,经过我作为程序员来讲已经磨炼的无比通透的大脑来讲发现这其实是个坑。

故事的起因是这样的,想开发一款给学生和老师用的产品,那么其中就少不了留作业的模块,所以咯,老师留作业,学生肯定会答作业,那么老师留作业肯定有参考答案,学生答作业肯定有学生答案,如果老师是教语文、英语、历史、政治啥的烂七八糟学科的 那么没问题,直接拿参考答案和学生答案的字符串去比较就OK了。

但是,如果是作业是数学、物理、化学的话那么就少不了使用公式了,这里前端使用的是MyScript 手写公式,非常强大可以将手写的公式转成LATEX格式的字符串。

前端将字符串获取到了,传到后台,这时候就需要我去解析做匹配了,计算学生的做题答案和教师的参考答案是否匹配。

我这里简单描述一下我的心路历程:

第一天,和同事简单沟通后,他向我推荐了一个Maple的组件,这个组件呢,说实话,通过运行cmd命令行中找到om.exe然后运行它,直接上图:

可以发现,假设a+b 为学生答案,b+a为教师的参考答案,那么直接一相减如果得0就说明计算结果是相同的,说明学生答案和参考答案是匹配的。

如果使用Maple还是要注意一点,他只识别[]中括号,所以要用之前还需对字符串进行处理,将所有的大括号全部替换成中括号。

悲哀的发现,如果使用Maple的话需要用到两个jar包,Maple.jar、externalcall.jar,启动这个引擎还需要将他用到的dll文件全部放到公共的库中,什么是公共的库,就是你配置在环境变量中的路径。可以是java/bin目录。如果这些全部都配好了,你会发现各种错误等待着你去解决....

正是因为使用这种第三方的组件,导致自己的程序过于冗余还需解决各种出现的问题,所以最终我放弃了。

第二天,我就在想能不能自己去写一个比对公式的帮助类,既轻量级又和现有项目无缝兼容。有了目标就需要有具体的想法,我在想,如何设计这个类,才能解决目前的问题呢。咱们从头再捋一遍,我的需求是:将两个字符进行公式匹配,如果最终能匹配上返回一个true,匹配不上返回false。

ok,我是这样设想的,首先我会预定义一些公式,然后用这些公式去匹配这2个字符串,记录每个公式出现的次数,如果公式出现的次数相同,则说明这2个字符串则有可能是相同的,如果公式出现的次数不同肯定不是相同的,直接返回false,那么这种匹配方式可以算为第一层的匹配。如果第一层公式次数匹配成功,则需要再进行深度匹配,何为深度匹配,就是需要将所有的公式替换成空字符串。将所有的符号,{、}、[、]、(、) 、\各种特殊的符号替换成空字符串,省下的就只有一些字母和数字以及加减乘除等运算符号了,接下来只需要将a-z的字母替换成数字 就可以进行计算了,这里我会预先将a-z设置为1-26的数字,然后循环替换掉,最终剩下的就是一组运算公式了,例如2a+2b 替换后为 21+22 这种东东,最后只需要计算一下即可,这里会用到javaScript引擎,因为它本事是弱类型脚本语言是支持这样计算的,并且好处是加减乘除的运行顺序已经帮你处理了,好了,说了这么多了,思路有了,接下来就开始实践吧,直接上干货。

公式匹配帮助类
 1 /**
 2      * 将字符串转义成replace能替换的字符.
 3      *
 4      * @Title: convertStrByRep
 5      * @author lmoran@163.com
 6      * @param str
 7      * @return
 8      */
 9     public static String convertStrByRep(String str) {
10         str=str.replaceAll("\\\\", "\\\\\\\\")
11                 .replaceAll("\\^", "\\\\\\^")
12                 .replaceAll("[{]", "[{]")
13                 .replaceAll("[}]", "[}]")
14                 .replaceAll("\\[", "\\[")
15                 .replaceAll("\\]", "\\]")
16                 .replaceAll("[(]", "[(]")
17                 .replaceAll("[(]", "[(]");
18         return str;
19     }
Util工具类中用到的方法
 1 package cfs.test;
 2 import cfs.core.util.MathUtil;
 3 public class Test {
 4     
 5     public static void main(String[] args) throws Throwable {
 6         String str1="a+b*b+d+\\sqrt {a^{2 }}(1){2}(3)\\pi";
 7         String str2="a+b*c+d+\\sqrt { a^ {2 }}\\pi";
 8         boolean bool=MathUtil.twoExprPattern(str1, str2);
 9         System.out.println(bool);
10         
11     }
12     
13 }
测试类

最后再附上一张运算后的截图:

posted @ 2016-05-19 17:43  刺风  阅读(979)  评论(0编辑  收藏  举报