在Sympy中收集表达式的类似项

8

我目前正在处理超过一个变量的函数,并需要收集类似项以简化表达式。

假设表达式如下所示:

x = sympy.Symbol('x')
y = sympy.Symbol('y')
k = sympy.Symbol('k')
a = sympy.Symbol('a')

z = k*(y**2*(a + x) + (a + x)**3/3) - k((2*k*y*(a + x)*(n - 1)*(-k*(y**2*(-a + x) + (-a + x)**3/3) + k*(y**2*(a + x) + (a + x)**3/3)) + y)**2*(-a + k*(n - 1)*(y**2 + (a + x)**2)*(-k*(y**2*(-a + x)))))
zEx = z.expand()
print type(z)
print type(zEx)

编辑:为了更加清晰,我调整了表达方式,使问题更容易理解。

假设 z 包含大量项,通过人工筛选并选择适当的项需要花费不可接受的时间。

我想收集所有仅为 a**1 的倍数的项。我不关心 a 的二次或更高次幂,也不关心不包含 a 的项。

zzEx 的类型如下:

print type(z)
print type(zEx)
>>>
<class 'sympy.core.add.Add'>
<class 'sympy.core.mul.Mul'>

有人知道我如何收集那些是a的倍数,但不是a^0或a^2的术语吗?

简而言之

在常数a和k下描述为zzEx及其type()的z(x,y)中,如何从z中删除所有非a项,并从表达式中删除所有二次或更高次的a项?这样剩下的只有包含a的单位幂的术语。

3个回答

7
除了其他给出的答案,您还可以将collect用作字典。
print(collect(zEx,a,evaluate=False)[a])

产生表达式的值
k*x**2 + k*y**2

2
最终只需要一行代码。@asmeurer 帮我找到了正确的方向(请查看本帖下面的评论)。以下是代码,解释可以在下面找到:
from sympy import *
from sympy.parsing.sympy_parser import parse_expr
import sys

x, y, k, a = symbols('x y k a')

# modified string: I added a few terms
z = x*(k*a**9) + (k**1)*x**2 - k*a**8 + y*x*(k**2) + y*(x**2)*k**3 + x*(k*a**1) - k*a**3 + y*a**5

zmod = Add(*[argi for argi in z.args if argi.has(a)])

那么zmod就是

a**9*k*x - a**8*k + a**5*y - a**3*k + a*k*x

那么让我们更仔细地看一下这个:

z.args

这只是您表达式中所有单独术语的集合(请注意,也解析了符号,这使事情变得更容易):

(k*x**2, a**5*y, -a**3*k, -a**8*k, a*k*x, a**9*k*x, k**2*x*y, k**3*x**2*y)

在列表推导中,您可以使用函数has选择所有包含a的项。然后可以使用Add将所有这些术语粘合在一起,从而得到所需的输出。 编辑 上面返回所有包含a的表达式。如果您只想过滤掉幂次为1的包含a的表达式,则可以使用collectMul
from sympy import *
from sympy.parsing.sympy_parser import parse_expr
import sys

x, y, k, a = symbols('x y k a')

z2 = x**2*(k*a**1) + (k**1)*x**2 - k*a**8 + y*x*(k**2) + y*(x**2)*k**3 + x*k*a - k*a**3 + y*a**1

zc = collect(z2, a, evaluate=False)
zmod2 = Mul(zc[a], a)

那么zmod2就是

a*(k*x**2 + k*x + y)

zmod2.expand()

a*k*x**2 + a*k*x + a*y

这是正确的。

使用您提供的更新的z,我运行:

z3 =  k*(y**2*(a + x) + (a + x)**3/3) - k((2*k*y*(a + x)*(n - 1)*(-k*(y**2*(-a + x) + (-a + x)**3/3) + k*(y**2*(a + x) + (a + x)**3/3)) + y)**2*(-a + k*(n - 1)*(y**2 + (a + x)**2)*(-k*(y**2*(-a + x)))))
zc3 = collect(z3.expand(), a, evaluate=False)
zmod3 = Mul(zc3[a], a)

然后获取 zmod3.expand() 的结果:

a*k*x**2 + a*k*y**2

这是您正在寻找的结果吗?

附注:感谢@asmeurer提供的所有有用评论!


1
如果您使用字符串来操作sympy表达式,那么您的方法是错误的。 - asmeurer
谢谢分享!当表达式全为正数时,将其拆分为字符串似乎很有效。但是如果表达式中有负项,我认为它不会正确地拆分项。如果每隔一个项都是负数,我该怎么做呢? - hkh
如果它们全部都是负数,你必须执行zstr.split('-')' - '.join(res)。如果既有+又有-,那就有点棘手了。我可以尝试稍后更新帖子。但根据@asmeurer和至少另一个人的说法,这不是正确的方法——即使它能完成任务,也会被投票降低评分。 - Cleb
在 SymPy 中,减法被表示为乘以 -1 的乘法运算。因此,x - y 等同于 Add(x, Mul(-1, y))。因此,如果按参数拆分它,符号已经在项上了,所以为了将它们重新组合在一起,你只需要执行 Add(*terms) - asmeurer
1
@Cleb 我取消了踩的评价。不过我建议直接移除旧回答,因为它仍然可以在编辑历史记录中看到。 - asmeurer
显示剩余7条评论

1
要遍历表达式的项,请使用expr.args
我不清楚a应该是什么,但collect函数可能会满足您的需求。

抱歉,应该将a定义为另一个变量:a = sympy.Symbol('a') - hkh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接