优化,还是不优化,这是个问题
本讨论仅基于程序基本上正确的情况下。
(一)第一次作业
众所周知,本次作业没有优化到100分的都进入了B组或者C组,所以事实上本次作业的优化是十分简单的,在这里提几句。
(1)合并同类项
最简单的做法也是最高效的做法,通过Hashmap,将指数存为key,将系数作为对应value,那么可以很简单的将同类项进行合并。
(2)对项进行省略
当然,+0,0*,1*,等这样无意义的项,仅需要做特殊判断,那么就可以直接省略,从而缩减表达式的长度。
(3)对项进行排序
对于负数,会比正数多一个不必要的符号,所以我们可以直接将负号作为减号放到项中,所以最好的做法是对所有在Hashmap里的系数做一个判断,先输出正项,若无正项,则输出负项。
经过以上三种处理,第一次作业便可以完美的将表达式化简为最简形式。
(二)第二次作业
本部分是本次讨论的重点部分。
第二次作业开始,大家的算法实现就变得五花八门,面向过程的,面向对象的,不一而足。
所以在这里我要基于一个看起来似乎是解决本次作业最好的算法来讨论化简。
根据题目可以得知,每一个原子项可以写成如下形式(请原谅我的超长正则):
(([+-]{0,3}\\d+)|([+-]{0,2}x(\\^[+-]?\\d+)?)|([+-]{0,2}sin\\(x\\)(\\^[+-]?\\d+)?)|([+-]{0,2}cos\\(x\\)(\\^[+-]?\\d+)?))(\\*(([+-]?\\d+)|(x(\\^[+-]?\\d+)?)|(sin\\(x\\)(\\^[+-]?\\d+)?)|(cos\\(x\\)(\\^[+-]?\\d+)?)))*
所以首先应该做的是对于原子项中的因子合并,从而形成形如a*x^b*sin(x)^c*cos(x)^d的形式,进而大大缩短了处理难度,下面放出我的合并函数:
for (String it : ob) {
if (it.matches("[+\\-]?\\d+")) {
num = num.multiply(new BigInteger(it));
}
else if (it.matches("[+-]?sin\\(x\\)(\\^[+-]?\\d+)?")) {
if (it.substring(0, 1).matches("-")) {
num = num.multiply(new BigInteger("-1"));
}
sin = "sin(x)^" + combine(sin, it);
}
else if (it.matches("[+-]?cos\\(x\\)(\\^[+-]?\\d+)?")) {
if (it.substring(0, 1).matches("-")) {
num = num.multiply(new BigInteger("-1"));
}
cos = "cos(x)^" + combine(cos, it);
}
else if (it.matches("[+-]?x(\\^[+-]?\\d+)?")) {
if (it.substring(0, 1).matches("-")) {
num = num.multiply(new BigInteger("-1"));
}
x = "x^" + combine(x, it);
}
}
private String combine(String sss, String it) {
Pattern p = Pattern.compile("\\^");
Matcher m = p.matcher(it);
BigInteger b1;
BigInteger b2;
if (sss.equals("")) {
if (m.find()) {
String t = it.substring(m.start() + 1);
return t;
}
else {
return "1";
}
}
else {
Pattern pp = Pattern.compile("\\^");
Matcher mm = pp.matcher(sss);
if (mm.find()) {
b1 = new BigInteger(sss.substring(mm.start() + 1));
if (m.find()) {
b2 = new BigInteger(it.substring(m.start() + 1));
}
else {
b2 = new BigInteger("1");
}
b1 = b1.add(b2);
return b1.toString();
}
}
return "0";
}
便将每一原子项中的同类因子合并,之后应该在项内做排序,从而得出如上述规范化的式子,即a*x^b*sin(x)^c*cos(x)^d的形式,这里就省略排序代码。
将通过+-分隔的每一原子项按照如上规则处理好之后,可以进行存入Hashmap处理,处理方式如下:
设立Poly类,其中包含BigInteger参数三个,分别为b、c、d,即x,sin(x),cos(x)各自的指数(若不存在此因子则指数为0),那么可以将此三元组作为key值,将a,即系数作为value,形成一个新的Hashmap,形式为Hashmap<Poly, BigInteger>,可知,对每个相同形式的项,均可以用前述方法处理并合并为同类项。
对于三角函数化简公式,比较简单的有如下几种:
sin(x)^2 + cos(x)^2 = 1
1 - cos(x)^2 = sin(x)^2
1 - sin(x)^2 = cos(x)^2
进阶版的有如下形式:
sin(x)^4 + 2 * sin(x) ^2* cos(x)^2 + cos(x)^4 = 1
sin(x)^4 - cos(x)^4 = sin(x)^2 - cos(x)^2
......
事实上,若想比较好的化简,应该先将进阶版部分的三角函数进行化简(即降次,否则会导致需要重复使用基础版化简公式,导致TLE)
由于我们的存储方式,可以知道,对于进阶版化简部分,可以枚举出几个典型的公式,进行if-else判断(方法繁琐但不容易出错),将常见的进阶版公式化简之后,就可以愉快的开始基础版化简了。
· 而根据我们之前所讨论的Hashmap存储方法,可知两对三元组为<b1, c1, d2>、<b2, c2, d2>,若满足:
1.b1 == b2
2.(abs(c1 - c2) == 2 && d1 == d2) || (abs(d2 - d1) == 2 && c1 == c2)
或
3.b1 == b2
2.(c1 - c2 == 2 && d1 - d2 == -2) || (d2 - d1 == 2 && c1 - c2 == -2)
即可将含有基础版化简形式的部分项化简,若分别考虑两者系数a1, a2,则会保留下某一三元组的部分式子。而上述基础版化简的时候要记得用if-else判断为哪种形式,否则化简出来的式子会含有^0之类不应该存在的部分。
在化简的时候需要注意的是,在Hashmap存化简之后的项的时候,要用keycontains检测是否已经存在了项,不要将已有的项覆盖掉:)
最后处理按照第一次作业类似的方法(排序,正项提前等)即可将化简做到比较好的程度。
(三)第三次作业
具体请见作业博客,网址如下:
https://www.cnblogs.com/dxy1999/p/10585219.html
最后, 我想说,其实化简表达式并不完全是为了分数,而更应该从中学到如何将OO课程学的更好。试问,如果在化简的时候对于自己的类的概念不清晰,又怎能写出正确的代码呢?疯狂面向过程写代码,又会创造出多么长的代码呢?愿我们共勉。
简,你化不化,它就在那里