傅洪光的三角函数简化¶
Fu 等人实现的 trigsimp 算法。
Fu 算法背后的思想是使用学生在预积分课程中学习的一系列规则。这些规则是启发式应用的,并且它使用贪心算法同时应用多个规则并选择叶数最少的结果。
在转换规则中,单个规则应用于表达式树。以下仅仅是助记性质的;请参阅文档字符串以获取示例。
TR0()
- 简化表达式TR1()
- sec-csc 到 cos-sinTR2()
- 正切-余切到正弦-余弦比TR2i()
- 正弦-余弦比到正切TR3()
- 角度规范化TR4()
- 在特殊角度下的函数TR5()
- 正弦的幂到余弦的幂TR6()
- 余弦的幂次转换为正弦的幂次TR7()
- 减少余弦功率(增加角度)TR8()
- 将正弦-余弦乘积展开为和TR9()
- 将正弦-余弦的和转换为乘积TR10()
- 分离 sin-cos 参数TR10i()
- 收集正弦-余弦参数TR11()
- 减少双角TR12()
- 分离 tan 参数TR12i()
- 收集 tan 参数TR13()
- 展开 tan-cot 的乘积TRmorrie()
- prod(cos(x*2**i), (i, 0, k - 1)) -> sin(2**k*x)/(2**k*sin(x))TR14()
- 将 sin 或 cos 的幂分解为 cos 或 sin 的幂TR15()
- 将正弦的负幂转换为余切的幂TR16()
- 余弦的负幂到正切的幂TR22()
- 将正切-余切幂转换为正割-余割函数的负幂TR111()
- 负的 sin-cos-tan 幂转换为 csc-sec-cot
有4种组合变换(CTR1 - CTR4),在这些变换中,应用一系列变换,并从几个选项中选择最简单的表达式。
最后,有2个规则列表(RL1和RL2),它们应用一系列的变换和组合变换,以及 fu
算法本身,该算法应用规则和规则列表并选择最佳表达式。还有一个函数 L
,它计算表达式中出现的三角函数的数量。
除了 TR0,表达式的重写不是由变换完成的。例如,TR10i 在和式中找到成对的项,这些项的形式类似于 cos(x)*cos(y) + sin(x)*sin(y)
。这样的表达式在表达式的自底向上遍历中被针对,但没有尝试进行操作使其出现。例如,
以下示例的设置:
>>> from sympy.simplify.fu import fu, L, TR9, TR10i, TR11
>>> from sympy import factor, sin, cos, powsimp
>>> from sympy.abc import x, y, z, a
>>> from time import time
>>> eq = cos(x + y)/cos(x)
>>> TR10i(eq.expand(trig=True))
-sin(x)*sin(y)/cos(x) + cos(y)
如果表达式被置于“正常”形式(带有公分母),那么转换是成功的:
>>> TR10i(_.normal())
cos(x + y)/cos(x)
TR11 的行为类似。它将双角重写为较小的角度,但不会对结果进行任何简化。
>>> TR11(sin(2)**a*cos(1)**(-a), 1)
(2*sin(1)*cos(1))**a/cos(1)**a
>>> powsimp(_)
(2*sin(1))**a
诱惑在于试图让这些TR规则“更智能”,但这实际上应该在更高层次上完成;TR规则应尽量保持“做好一件事”的原则。然而,有一个例外。在TR10i和TR9中,即使每个术语都乘以一个公因子,它们也能被识别:
>>> fu(a*cos(x)*cos(y) + a*sin(x)*sin(y))
a*cos(x - y)
使用 factor_terms
进行因式分解,但它类似于“JIT”,即延迟到被认为有必要时才进行。此外,如果因式分解无助于简化,则不会保留,因此 a*cos(x)*cos(y) + a*sin(x)*sin(z)
不会变成因式分解(但在三角函数意义上未简化)的表达式:
>>> fu(a*cos(x)*cos(y) + a*sin(x)*sin(z))
a*sin(x)*sin(z) + a*cos(x)*cos(y)
在某些情况下,分解可能是一个好主意,但用户需要自己做出这个决定。例如:
>>> expr=((15*sin(2*x) + 19*sin(x + y) + 17*sin(x + z) + 19*cos(x - z) +
... 25)*(20*sin(2*x) + 15*sin(x + y) + sin(y + z) + 14*cos(x - z) +
... 14*cos(y - z))*(9*sin(2*y) + 12*sin(y + z) + 10*cos(x - y) + 2*cos(y -
... z) + 18)).expand(trig=True).expand()
在展开状态下,存在近1000个三角函数:
>>> L(expr)
932
如果首先对表达式进行因式分解,这会花费时间,但得到的表达式会非常快速地被转换:
>>> def clock(f, n=2):
... t=time(); f(); return round(time()-t, n)
...
>>> clock(lambda: factor(expr))
0.86
>>> clock(lambda: TR10i(expr), 3)
0.016
如果使用未展开的表达式,转换所需的时间会更长,但不会像先因式分解再转换那样长:
>>> clock(lambda: TR10i(expr), 2)
0.28
因此,在 TR10i
中既不使用展开也不使用因式分解:如果表达式已经是因式分解的(或部分因式分解的),那么使用 trig=True
进行展开会破坏已知的内容并花费更长时间;如果表达式是展开的,因式分解可能比直接应用变换本身花费更长时间。
尽管这些算法应该是规范的,总是给出相同的结果,但它们可能不会产生最佳结果。一般来说,这是简化的本质,因为搜索所有可能的变换路径是非常昂贵的。这里有一个简单的例子。以下和中有6项:
>>> expr = (sin(x)**2*cos(y)*cos(z) + sin(x)*sin(y)*cos(x)*cos(z) +
... sin(x)*sin(z)*cos(x)*cos(y) + sin(y)*sin(z)*cos(x)**2 + sin(y)*sin(z) +
... cos(y)*cos(z))
>>> args = expr.args
意外地,fu 给出了最好的结果:
>>> fu(expr)
3*cos(y - z)/2 - cos(2*x + y + z)/2
但如果将不同的术语结合起来,可能会得到一个不太理想的结果,需要一些额外的工作来获得更好的简化,但仍然不如理想。以下展示了一种 expr
的替代形式,一旦采取了某个步骤,它就会阻碍最佳简化,因为它会导致死胡同:
>>> TR9(-cos(x)**2*cos(y + z) + 3*cos(y - z)/2 +
... cos(y + z)/2 + cos(-2*x + y + z)/4 - cos(2*x + y + z)/4)
sin(2*x)*sin(y + z)/2 - cos(x)**2*cos(y + z) + 3*cos(y - z)/2 + cos(y + z)/2
这是一个展示相同行为的简短表达式:
>>> a = sin(x)*sin(z)*cos(x)*cos(y) + sin(x)*sin(y)*cos(x)*cos(z)
>>> TR10i(a)
sin(x)*sin(y + z)*cos(x)
>>> newa = _
>>> TR10i(expr - a) # this combines two more of the remaining terms
sin(x)**2*cos(y)*cos(z) + sin(y)*sin(z)*cos(x)**2 + cos(y - z)
>>> TR10i(_ + newa) == _ + newa # but now there is no more simplification
True
如果不走运或尝试所有可能的参数配对,最终结果可能不尽如人意,且在没有更好的启发式方法或暴力尝试所有可能性之前,无法找到。
规则¶
- sympy.simplify.fu.TR1(rv)[源代码][源代码]¶
将 sec, csc 替换为 1/cos, 1/sin
示例
>>> from sympy.simplify.fu import TR1, sec, csc >>> from sympy.abc import x >>> TR1(2*csc(x) + sec(x)) 1/cos(x) + 2/sin(x)
- sympy.simplify.fu.TR2(rv)[源代码][源代码]¶
将 tan 和 cot 替换为 sin/cos 和 cos/sin
示例
>>> from sympy.simplify.fu import TR2 >>> from sympy.abc import x >>> from sympy import tan, cot, sin, cos >>> TR2(tan(x)) sin(x)/cos(x) >>> TR2(cot(x)) cos(x)/sin(x) >>> TR2(tan(tan(x) - sin(x)/cos(x))) 0
- sympy.simplify.fu.TR2i(rv, half=False)[源代码][源代码]¶
- 将涉及 sin 和 cos 的比率转换如下:
sin(x)/cos(x) -> tan(x) sin(x)/(cos(x) + 1) -> tan(x/2) 如果 half=True
示例
>>> from sympy.simplify.fu import TR2i >>> from sympy.abc import x, a >>> from sympy import sin, cos >>> TR2i(sin(x)/cos(x)) tan(x)
分子和分母的幂次也被识别
>>> TR2i(sin(x)**2/(cos(x) + 1)**2, half=True) tan(x/2)**2
除非假设允许(即,对于分子和分母,底数必须为正数或指数必须为整数),否则转换不会发生。
>>> TR2i(sin(x)**a/(cos(x) + 1)**a) sin(x)**a/(cos(x) + 1)**a
- sympy.simplify.fu.TR3(rv)[源代码][源代码]¶
诱导公式:示例 sin(-a) = -sin(a)
示例
>>> from sympy.simplify.fu import TR3 >>> from sympy.abc import x, y >>> from sympy import pi >>> from sympy import cos >>> TR3(cos(y - x*(y - x))) cos(x*(x - y) + y) >>> cos(pi/2 + x) -sin(x) >>> cos(30*pi/2 + x) -cos(x)
- sympy.simplify.fu.TR4(rv)[源代码][源代码]¶
识别特殊角度的值。
示例
>>> from sympy import pi >>> from sympy import cos, sin, tan, cot >>> for s in (0, pi/6, pi/4, pi/3, pi/2): ... print('%s %s %s %s' % (cos(s), sin(s), tan(s), cot(s))) ... 1 0 0 zoo sqrt(3)/2 1/2 sqrt(3)/3 sqrt(3) sqrt(2)/2 sqrt(2)/2 1 1 1/2 sqrt(3)/2 sqrt(3) sqrt(3)/3 0 1 zoo 0
- sympy.simplify.fu.TR5(rv, max=4, pow=False)[源代码][源代码]¶
用 1 - cos(x)**2 替换 sin**2。
参见 _TR56 文档字符串以了解
max
和pow
的高级用法。示例
>>> from sympy.simplify.fu import TR5 >>> from sympy.abc import x >>> from sympy import sin >>> TR5(sin(x)**2) 1 - cos(x)**2 >>> TR5(sin(x)**-2) # unchanged sin(x)**(-2) >>> TR5(sin(x)**4) (1 - cos(x)**2)**2
- sympy.simplify.fu.TR6(rv, max=4, pow=False)[源代码][源代码]¶
用 1 - sin(x)**2 替换 cos**2。
参见 _TR56 文档字符串以了解
max
和pow
的高级用法。示例
>>> from sympy.simplify.fu import TR6 >>> from sympy.abc import x >>> from sympy import cos >>> TR6(cos(x)**2) 1 - sin(x)**2 >>> TR6(cos(x)**-2) #unchanged cos(x)**(-2) >>> TR6(cos(x)**4) (1 - sin(x)**2)**2
- sympy.simplify.fu.TR7(rv)[源代码][源代码]¶
降低 cos(x)**2 的次数。
示例
>>> from sympy.simplify.fu import TR7 >>> from sympy.abc import x >>> from sympy import cos >>> TR7(cos(x)**2) cos(2*x)/2 + 1/2 >>> TR7(cos(x)**2 + 1) cos(2*x)/2 + 3/2
- sympy.simplify.fu.TR8(rv, first=True)[源代码][源代码]¶
将
cos
和/或sin
的乘积转换为cos
和/或sin
项的和或差。示例
>>> from sympy.simplify.fu import TR8 >>> from sympy import cos, sin >>> TR8(cos(2)*cos(3)) cos(5)/2 + cos(1)/2 >>> TR8(cos(2)*sin(3)) sin(5)/2 + sin(1)/2 >>> TR8(sin(2)*sin(3)) -cos(5)/2 + cos(1)/2
- sympy.simplify.fu.TR9(rv)[源代码][源代码]¶
cos
或sin
项的和作为cos
或sin
的乘积。示例
>>> from sympy.simplify.fu import TR9 >>> from sympy import cos, sin >>> TR9(cos(1) + cos(2)) 2*cos(1/2)*cos(3/2) >>> TR9(cos(1) + 2*sin(1) + 2*sin(2)) cos(1) + 4*sin(3/2)*cos(1/2)
如果 TR9 没有进行任何更改,则不会对表达式进行重新排列。例如,尽管尝试了对公共项进行因式分解,但如果因式分解后的表达式没有变化,将返回原始表达式:
>>> TR9(cos(3) + cos(3)*cos(2)) cos(3) + cos(2)*cos(3)
- sympy.simplify.fu.TR10(rv, first=True)[源代码][源代码]¶
在
cos
和sin
中分开求和。示例
>>> from sympy.simplify.fu import TR10 >>> from sympy.abc import a, b, c >>> from sympy import cos, sin >>> TR10(cos(a + b)) -sin(a)*sin(b) + cos(a)*cos(b) >>> TR10(sin(a + b)) sin(a)*cos(b) + sin(b)*cos(a) >>> TR10(sin(a + b + c)) (-sin(a)*sin(b) + cos(a)*cos(b))*sin(c) + (sin(a)*cos(b) + sin(b)*cos(a))*cos(c)
- sympy.simplify.fu.TR10i(rv)[源代码][源代码]¶
积和式到和函数的转换。
示例
>>> from sympy.simplify.fu import TR10i >>> from sympy import cos, sin, sqrt >>> from sympy.abc import x
>>> TR10i(cos(1)*cos(3) + sin(1)*sin(3)) cos(2) >>> TR10i(cos(1)*sin(3) + sin(1)*cos(3) + cos(3)) cos(3) + sin(4) >>> TR10i(sqrt(2)*cos(x)*x + sqrt(6)*sin(x)*x) 2*sqrt(2)*x*sin(x + pi/6)
- sympy.simplify.fu.TR11(rv, base=None)[源代码][源代码]¶
双角到乘积的函数。
base
参数可以用来指示未加倍的角度是什么,例如,如果 3*pi/7 是基数,那么参数为 6*pi/7 的余弦和正弦函数将被替换。示例
>>> from sympy.simplify.fu import TR11 >>> from sympy import cos, sin, pi >>> from sympy.abc import x >>> TR11(sin(2*x)) 2*sin(x)*cos(x) >>> TR11(cos(2*x)) -sin(x)**2 + cos(x)**2 >>> TR11(sin(4*x)) 4*(-sin(x)**2 + cos(x)**2)*sin(x)*cos(x) >>> TR11(sin(4*x/3)) 4*(-sin(x/3)**2 + cos(x/3)**2)*sin(x/3)*cos(x/3)
如果参数只是整数,除非提供了基数,否则不会进行任何更改:
>>> TR11(cos(2)) cos(2) >>> TR11(cos(4), 2) -sin(2)**2 + cos(2)**2
这里有一个微妙的问题,即自动简化会将一些较大的角度转换为较小的角度。
>>> cos(6*pi/7) + cos(3*pi/7) -cos(pi/7) + cos(3*pi/7)
6*pi/7 角现在为 pi/7,但可以通过提供 3*pi/7 基底来使用 TR11 进行目标定位:
>>> TR11(_, 3*pi/7) -sin(3*pi/7)**2 + cos(3*pi/7)**2 + cos(3*pi/7)
- sympy.simplify.fu.TR12(rv, first=True)[源代码][源代码]¶
在
tan
中分离和。示例
>>> from sympy.abc import x, y >>> from sympy import tan >>> from sympy.simplify.fu import TR12 >>> TR12(tan(x + y)) (tan(x) + tan(y))/(-tan(x)*tan(y) + 1)
- sympy.simplify.fu.TR12i(rv)[源代码][源代码]¶
将 tan 参数组合为 (tan(y) + tan(x))/(tan(x)*tan(y) - 1) -> -tan(x + y)。
示例
>>> from sympy.simplify.fu import TR12i >>> from sympy import tan >>> from sympy.abc import a, b, c >>> ta, tb, tc = [tan(i) for i in (a, b, c)] >>> TR12i((ta + tb)/(-ta*tb + 1)) tan(a + b) >>> TR12i((ta + tb)/(ta*tb - 1)) -tan(a + b) >>> TR12i((-ta - tb)/(ta*tb - 1)) tan(a + b) >>> eq = (ta + tb)/(-ta*tb + 1)**2*(-3*ta - 3*tc)/(2*(ta*tc - 1)) >>> TR12i(eq.expand()) -3*tan(a + b)*tan(a + c)/(2*(tan(a) + tan(b) - 1))
- sympy.simplify.fu.TR13(rv)[源代码][源代码]¶
改变
tan
或cot
的乘积。示例
>>> from sympy.simplify.fu import TR13 >>> from sympy import tan, cot >>> TR13(tan(3)*tan(2)) -tan(2)/tan(5) - tan(3)/tan(5) + 1 >>> TR13(cot(3)*cot(2)) cot(2)*cot(5) + 1 + cot(3)*cot(5)
- sympy.simplify.fu.TRmorrie(rv)[源代码][源代码]¶
返回 cos(x)*cos(2*x)*…*cos(2**(k-1)*x) -> sin(2**k*x)/(2**k*sin(x))
参考文献
示例
>>> from sympy.simplify.fu import TRmorrie, TR8, TR3 >>> from sympy.abc import x >>> from sympy import Mul, cos, pi >>> TRmorrie(cos(x)*cos(2*x)) sin(4*x)/(4*sin(x)) >>> TRmorrie(7*Mul(*[cos(x) for x in range(10)])) 7*sin(12)*sin(16)*cos(5)*cos(7)*cos(9)/(64*sin(1)*sin(3))
有时自动简化会导致无法识别幂。例如,在以下情况下,cos(4*pi/7) 自动简化为 -cos(3*pi/7),因此只有3个项中的2个被识别:
>>> TRmorrie(cos(pi/7)*cos(2*pi/7)*cos(4*pi/7)) -sin(3*pi/7)*cos(3*pi/7)/(4*sin(pi/7))
TR8 的一次触碰将表达式解析为一个有理数
>>> TR8(_) -1/8
在这种情况下,如果 eq 未简化,答案可以直接获得:
>>> eq = cos(pi/9)*cos(2*pi/9)*cos(3*pi/9)*cos(4*pi/9) >>> TRmorrie(eq) 1/16
但如果使用TR3使角度规范化,那么答案在没有进一步工作的情况下不会被简化:
>>> TR3(eq) sin(pi/18)*cos(pi/9)*cos(2*pi/9)/2 >>> TRmorrie(_) sin(pi/18)*sin(4*pi/9)/(8*sin(pi/9)) >>> TR8(_) cos(7*pi/18)/(16*sin(pi/9)) >>> TR3(_) 1/16
原始表达式本应直接通过 TR8 解析为 1/16,然而:
>>> TR8(eq) 1/16
- sympy.simplify.fu.TR14(rv, first=True)[源代码][源代码]¶
将分解的正弦和余弦幂次方转换为更简单的表达式。
示例
>>> from sympy.simplify.fu import TR14 >>> from sympy.abc import x, y >>> from sympy import cos, sin >>> TR14((cos(x) - 1)*(cos(x) + 1)) -sin(x)**2 >>> TR14((sin(x) - 1)*(sin(x) + 1)) -cos(x)**2 >>> p1 = (cos(x) + 1)*(cos(x) - 1) >>> p2 = (cos(y) - 1)*2*(cos(y) + 1) >>> p3 = (3*(cos(y) - 1))*(3*(cos(y) + 1)) >>> TR14(p1*p2*p3*(x - 1)) -18*(x - 1)*sin(x)**2*sin(y)**4
- sympy.simplify.fu.TR15(rv, max=4, pow=False)[源代码][源代码]¶
将 sin(x)**-2 转换为 1 + cot(x)**2。
参见 _TR56 文档字符串以了解
max
和pow
的高级用法。示例
>>> from sympy.simplify.fu import TR15 >>> from sympy.abc import x >>> from sympy import sin >>> TR15(1 - 1/sin(x)**2) -cot(x)**2
- sympy.simplify.fu.TR16(rv, max=4, pow=False)[源代码][源代码]¶
将 cos(x)**-2 转换为 1 + tan(x)**2。
参见 _TR56 文档字符串以了解
max
和pow
的高级用法。示例
>>> from sympy.simplify.fu import TR16 >>> from sympy.abc import x >>> from sympy import cos >>> TR16(1 - 1/cos(x)**2) -tan(x)**2
- sympy.simplify.fu.TR111(rv)[源代码][源代码]¶
将 f(x)**-i 转换为 g(x)**i,其中
i
是整数或底数为正,且 f, g 分别为:tan, cot; sin, csc; 或 cos, sec。示例
>>> from sympy.simplify.fu import TR111 >>> from sympy.abc import x >>> from sympy import tan >>> TR111(1 - 1/tan(x)**2) 1 - cot(x)**2
- sympy.simplify.fu.TR22(rv, max=4, pow=False)[源代码][源代码]¶
将 tan(x)**2 转换为 sec(x)**2 - 1 ,将 cot(x)**2 转换为 csc(x)**2 - 1。
参见 _TR56 文档字符串以了解
max
和pow
的高级用法。示例
>>> from sympy.simplify.fu import TR22 >>> from sympy.abc import x >>> from sympy import tan, cot >>> TR22(1 + tan(x)**2) sec(x)**2 >>> TR22(1 + cot(x)**2) csc(x)**2
- sympy.simplify.fu.TRpower(rv)[源代码][源代码]¶
将 sin(x)**n 和 cos(x)**n 转换为正 n 的和。
参考文献
示例
>>> from sympy.simplify.fu import TRpower >>> from sympy.abc import x >>> from sympy import cos, sin >>> TRpower(sin(x)**6) -15*cos(2*x)/32 + 3*cos(4*x)/16 - cos(6*x)/32 + 5/16 >>> TRpower(sin(x)**3*cos(2*x)**4) (3*sin(x)/4 - sin(3*x)/4)*(cos(4*x)/2 + cos(8*x)/8 + 3/8)
- sympy.simplify.fu.fu(rv, measure=<function <lambda>>)[源代码][源代码]¶
尝试通过使用Fu等人在算法中给出的转换规则来简化表达式。
fu()
将尝试最小化目标函数measure
。默认情况下,首先最小化三角项的数量,然后最小化总操作数。参考文献
示例
>>> from sympy.simplify.fu import fu >>> from sympy import cos, sin, tan, pi, S, sqrt >>> from sympy.abc import x, y, a, b
>>> fu(sin(50)**2 + cos(50)**2 + sin(pi/6)) 3/2 >>> fu(sqrt(6)*cos(x) + sqrt(2)*sin(x)) 2*sqrt(2)*sin(x + pi/3)
CTR1 示例
>>> eq = sin(x)**4 - cos(y)**2 + sin(y)**2 + 2*cos(x)**2 >>> fu(eq) cos(x)**4 - 2*cos(y)**2 + 2
CTR2 示例
>>> fu(S.Half - cos(2*x)/2) sin(x)**2
CTR3 示例
>>> fu(sin(a)*(cos(b) - sin(b)) + cos(a)*(sin(b) + cos(b))) sqrt(2)*sin(a + b + pi/4)
CTR4 示例
>>> fu(sqrt(3)*cos(x)/2 + sin(x)/2) sin(x + pi/3)
示例 1
>>> fu(1-sin(2*x)**2/4-sin(y)**2-cos(x)**4) -cos(x)**2 + cos(y)**2
示例 2
>>> fu(cos(4*pi/9)) sin(pi/18) >>> fu(cos(pi/9)*cos(2*pi/9)*cos(3*pi/9)*cos(4*pi/9)) 1/16
示例 3
>>> fu(tan(7*pi/18)+tan(5*pi/18)-sqrt(3)*tan(5*pi/18)*tan(7*pi/18)) -sqrt(3)
目标函数示例
>>> fu(sin(x)/cos(x)) # default objective function tan(x) >>> fu(sin(x)/cos(x), measure=lambda x: -x.count_ops()) # maximize op count sin(x)/cos(x)
注释¶
这项工作由Dimitar Vlahovski在“电子系统”技术学校开始(2011年11月30日)。
除了TR13之外,其他规则并非来自原始论文,而是在SymPy中扩展的。