前情提要---------算了我感觉大家就知道就不说了。
那么就直接步入正题吧,如何优化结果,即使得输出长度尽可能短一些。
也就是在规定的格式范围并保证等价的情况下,尽量简化一个含有幂函数与三角函数的表达式,化简操作对应的主要是三角函数。(下述优化建立在已经可以实现合并同类项的基础上)
在仅包含正弦与余弦函数的情况下,可以使用的三角关系相当有限,大部分化简都是如下的考量,将形如:
poly*(sin(x)^2+cos(x)^2)
的式子,简化为
poly
其中,poly对应的是多项式,即表达式。
那么,问题就是,怎样在一个多项式中找到一个子多项式,可以化简为上述形式并使得poly的项数尽可能的大呢?
很容易想到的是,先用单个的项与其他的项进行合并。即先把
mono*(sin(x)^2+cos(x)^2)
的式子,简化为
mono
用一个四元组(c,xe,se,ce)表示一个项,各字母的意义分别是,系数、x的指数,sin(x)的指数,cos(x)的指数。
对于一个单项mono1(c1,xe1,se1,ce1),与另外一个单项mono2(c2,xe2,se2,ce2),如果满足以下条件,即可合并。
(1) xe1=xe2,即,x对应的指数必须相同
(2) c1与c2符号相同(保证稳定性)
(3) se1-se2==2 & ce2-ce1==2 | se2-se1==2 & ce1-ce2==2
满足上述条件,不妨设c1与c2均为正,c1为较小的系数,那么就可以将两项合并为(c1,xe1,min(se1,se2),min(ce1,ce2)),再加一个余项(c2-c1,xe2,se2,ce2)。
简单来说,就是把mono2拆成两项,一项与mono1合并,并且提取出公因式mono,另外还有一个余项mono3跟在后面,经过化简,mono1与mono2变成了mono与mono3。
那么长度是否减小了呢?
由于mono的系数与mono1相同,指数小于mono1,故mono的长度必定是小于等于mono1的。
由于mono3的指数与mono2相同,系数小于mono2,故mono3的长度必定是小于等于mono2的。
所以上述化简方法确实可以使得长度逐渐减小,并且在系数相同时,达到了最佳的输出(余项为0)。
那么约定符号相同也很容易理解了,如果两项符号不同,可能出现化简后长度反增的情况:
9*sin(x)^12*cos(x)^12-2*sin(x)^10*cos(x)^14
经上述化简后:
11*sin(x)^12*cos(x)^12-2*sin(x)^10*cos(x)^12
化简后长度反而变长了,这是由于一项系数的绝对值增加,当指数的长度的减小(或不变)不足以抵消掉系数长度的增加时,输出长度可能会增加。
所以如果要保证达到最优效果,上述方法应该放宽符号限制,当系数符号相反时,根据具体的系数与指数的值,决定是否合并。
这就是基本的优化思路,但是还不够,因为我们发现:
cos(x)^4-sin(x)^4
用上述方法是无法优化的,但是
cos(x)^4-sin(x)^4=(cos(x)^2-sin(x)^2)*(cos(x)^2+sin(x)^2)
=cos(x)^2-sin(x)^2
=1-2*sin(x)^2
是可以化简为更短的一个式子的,但是其不满足上述条件(3),无法使用上述方法优化。
那么怎么优化呢?
回到最前面的问题,我们的目标是找到一个包含尽可能多项的一个poly,将poly*(sin(x)^2+cos(x)^2),化简为poly。
在上面,我们采用了分析项与项之间关系的方法,这与直接提取多项式是不同的。
简单来说,因式分解时,以
a^2-b^2=(a-b)*(a+b)
为例,左侧并不直接包含a*(a+b)与-b*(a+b)两项,所以我们去直接提取a+b的话是无法得到结果的。作为人,我们知道可以加上a*b-a*b一项,并不改变左侧等式的值,就可以把左侧变化为右侧的形式,那么能不能以同样的方法去写程序呢?
下面是笔者由于种种原因没有动笔的思路,所以可能会存在一点点问题,如果有的话希望大家及时指出。
-------------------------------------------分割线--------------------------------------------------------
设项(c,xe,se,ce)(c!=0):
若se>=2,定义其sin辅助项为(-c,xe,se-2,ce+2),结果项为(c,xe,se-2,ce)
若ce>=2,定义其cos辅助项为(-c,xe,se+2,ce-2),结果项为(c,xe,se,ce-2)
辅助项的意义与上述a*b的意义相同,在于为合并提供一座“桥梁”,沟通一些本不能合并的项。结果项的意义是与辅助项的相反合并后的公因式。
这样,一个项就可以表示为其对于结果项与辅助项的和。
以cos(x)^4为例,其cos辅助项为-cos(x)^2*sin(x)^2,其结果项为cos(x)^2,过程如下:
cos(x)^4=cos(x)^4+cos(x)^2*sin(x)^2-cos(x)^2*sin(x)^2
=cos(x)^2*(sin(x)^2-cos(x)^2)-cos(x)^2*sin(x)^2
=cos(x)^2-cos(x)^2*sin(x)^2
这只是一个辅助理解的式子,并没有使式子得到简化。那么什么条件下才可以简化长度呢?
我们尝试以如下方法去遍历一个多项式中的每个项mono:
若mono的se>=2,依次判断:
(1)查询其sin辅助项能否与其他项进行合并同类项,可以的话将mono更新为其结果项,将辅助项合并。
(2)查询其sin结果项能否与其他项进行合并同类项,可以的话讲mono更新为其辅助项,将结果项合并。特殊地,如果辅助项已在第一步合并,直接合并结果项,将mono项更新为0即可。
若均无法合并,再去同理判断ce即可
那么这样的合并一定会使长度减小吗?
答案其实是不一定的,也需要去判断对应的指数与系数的长度。当结果项与辅助项都可以合并时,长度是一定会减少的,但是当只有其中的一项可以合并时,长度可能会增加也可能会减少。
所以上述方法应该变一下:
(1)若se>=2,判断其sin辅助项与结果项是否均可合并,若是,则合并,判断下一项。
(2)若否,如果ce>=2,判断cos辅助项与结果项是否均可合并,若是,则合并,判断下一项。
(3)若(1)与(2)中的答案均为否,再去判断单独合并的情况。
判断单独合并时,就要判断系数与指数的值,决定是否合并(与刚开始的符号相反的情况原理相同)。
这样的合并已经可以满足大部分情况了,但是好像还不大够,显得有一点“目光短浅”了。
考虑下面这种情况:
若mono的sin结果项可以与其他合并,辅助项不可以,此时合并会使得长度+1,所以拒绝了本次合并。但是有另外一个项mono1,它的cos辅助项可以合并,结果项可以与mono的sin辅助项合并(无法与其他项合并)。
此时,拒绝掉mono的合并会使得后面mono1也无法合并,这样就失去了合并的机会。例如:
sin(x)^8的sin辅助项-sin(x)^6*cos(x)^2可以与sin(x)^6*cos(x)^4的cos结果项sin(x)^6*cos(x)^2合并 (其他项略)
这个时候如果还要想进一步优化该怎么办呢?
细心的你肯定已经注意到了,上述对于合并的概念还停留在合并同类项的层面,如果对于辅助项/结果项与其他项的合并,也采用划分为结果项与辅助项的思路,是不是就可以合并了?
当然,这样一路下去是个无底洞,笔者就不继续分析了。优化方法的复杂度与收益已经远远不成正比了,追求极致的优化几乎是不可能的。