为啥 SymPy 不将 (-x**3)**(2/3) 简化为 x**2?

Posted

技术标签:

【中文标题】为啥 SymPy 不将 (-x**3)**(2/3) 简化为 x**2?【英文标题】:Why does SymPy not simplify (-x**3)**(2/3) to x**2?为什么 SymPy 不将 (-x**3)**(2/3) 简化为 x**2? 【发布时间】:2020-09-11 19:53:16 【问题描述】:

我正在尝试使用 SymPy 来评估以下积分:

手动评估时,答案是 −½log(28)。

在我与 x 整合之前,我的工作与 SymPy 相匹配:

x, y = sp.symbols('x y', real=True)
z = 1 / (sp.root(y, 3)*(x**3+1))
iz = z.integrate((y, -x**3, 0)) # integrate with respect to y
print(iz)
# -3*(-x**3)**(2/3)/(2*(x**3 + 1))
iiz = iz.integrate((x, 0, 3)) # integrate with respect to x
print(iiz)
# -3*Integral((-x**3)**(2/3)/(x**3 + 1), (x, 0, 3))/2
print(sp.N(iiz))
# 0.833051127543801 - 1.4428868782084*I

看来让 SymPy 失望的是(-x**3)**(2/3)。这应该简化为x**2,但 SymPy 不这么认为。手动简化,产生与我手动得到的相同答案:

print( sp.integrate(-3*x**2/(2*(x**3 + 1)), (x, 0, 3)) )
# -log(28)/2

有没有更好的方法来解决这个问题?

【问题讨论】:

我快速浏览了docs.sympy.org/latest/tutorial/simplification.html#powers 默认情况下未执行的简化,但不幸的是它们都没有改变任何东西。您可能应该在github上查询github.com/sympy/sympy/issues abs() 匹配。 iiz 有一个 (-1)**(2/3) 术语,这使得结果复杂。 @hpaulj 关于abs() 的观察真的很有趣。这是否意味着-log(28)/20.833051127543801 - 1.4428868782084*I 都是有效答案,因为它们在复平面中与原点的距离相同? @nvi:虽然 −½log(28) 和 0.83 − 1.44i 是正确答案,但这并不是因为它们具有相同的绝对值(到原点的距离)。它只是来自这个特定方程的特殊属性的必需属性。具有该属性的数字有无数个,其中最多三个是解(取决于方程式中 ^⅓ 的定义,是否允许复杂的解等等)。此外,对于其他问题,解决方案可能具有不同的绝对值。 【参考方案1】:

您的问题是 sympy.root 默认返回主根,而不是真正的根。为避免这种情况,您可以使用sympy.root 的第三个可选参数来指定您想要真正的根。以下产生了预期的结果:

import sympy as sp

x, y = sp.symbols('x y', real=True)
z = 1 / (sp.root(y,3,1)*(x**3+1))
iz = z.integrate((y, -x**3, 0))
iiz = iz.integrate((x, 0, 3))
print(iiz)
# -log(28)/2

为了稍微解决您的名义问题,(-x**3)**(2/3) 实际上是(-x**3)**0.666666666666667,因为这是您那里的 Python 分数。要获得更接近您想要的东西,您需要这样做:

import sympy as sp
x = sp.symbols('x', positive=True)
solution = (-x**3)**sp.Rational(2,3)
print(solution)
# (-1)**(2/3)*x**2

一般来说,我建议避免使用理性权力,除非你真的需要在它们的多种解决方案、复杂性等中考虑它们。

【讨论】:

运行你的第一个 sn-p 显示 sp.N(iiz) = -1.6661022550876 + 2.71050543121376e-20*I。自从 sp.N(-sp.log(28)/2) = -1.66610225508760 以来,这更接近我所期待的答案,但我不清楚那个微小的虚数部分来自哪里。据我了解,复数没有等效的实数形式,所以我很困惑,我对同一个问题有两个不同的答案。 @nvi:我无法重现。第一个代码返回-log(28)/2给我,sympy.N变成-1.66610225508760 有趣,这可能与使用 Sympy 版本 1.1.1 的 Colab 有关。我已经复制了你的代码here。感谢您的帮助。【参考方案2】:

在我的isympy 会话中:SymPy 1.6.2

In [131]: z = 1 / (root(y,3)*(x**3+1))

In [132]: iz = z.integrate((y, -x**3, 0))

In [133]: iiz = iz.integrate((x,0,3))

In [134]: iiz
Out[134]: 
     2/3         
-(-1)   ⋅log(28) 
─────────────────
        2        

In [135]: N(iiz)
Out[135]: 0.833051127543801 - 1.4428868782084⋅ⅈ

In [136]: abs(iiz)
Out[136]: 
log(28)
───────
   2   

root 文档谈到返回主根,除了提供k 参数外,建议使用real_root

In [137]: z = 1 / (real_root(y,3)*(x**3+1))

In [138]: iz = z.integrate((y, -x**3, 0))

In [139]: iiz = iz.integrate((x,0,3))

In [140]: iiz
Out[140]: 
-log(28) 
─────────
    2    

In [141]: N(iiz)
Out[141]: -1.66610225508760

显然,双积分有多个解,具体取决于根。看起来它们都具有相同的大小。这听起来很合理,但我复杂的数学研究是在遥远的过去,所以我无法提供理论依据。

有了k=2,我们得到了第三种解决方案:

In [146]: z = 1 / (root(y,3,2)*(x**3+1))

In [147]: iz = z.integrate((y, -x**3, 0))

In [148]: iiz = iz.integrate((x,0,3))

    In [149]: iiz
    Out[149]: 
    3 ____        
    ╲╱ -1 ⋅log(28)
    ──────────────
          2     

所以复平面上有3个解,乘数为-1, (-1)**(1/3), -(-1)**(2/3),量级相同。

-1.66610225508760
0.833051127543801 - 1.4428868782084⋅ⅈ
0.833051127543801 + 1.4428868782084⋅ⅈ

如果我们将整数符号k 引入z

In [158]: z = 1 / (root(y,3,k)*(x**3+1))

In [159]: z
Out[159]: 
      -2⋅k    
      ─────   
        3     
  (-1)        
──────────────
3 ___ ⎛ 3    ⎞
╲╱ y ⋅⎝x  + 1⎠

二重积分变为:

In [164]: iiz =z.integrate((y, -x**3,0)).integrate((x,0,3))

In [165]: iiz
Out[165]: 
             -2⋅k          
             ─────         
     2/3       3           
-(-1)   ⋅(-1)     ⋅log(28) 
───────────────────────────
             2             

并做iiz.subs(k:0) 等,产生上述复杂的解决方案。

【讨论】:

以上是关于为啥 SymPy 不将 (-x**3)**(2/3) 简化为 x**2?的主要内容,如果未能解决你的问题,请参考以下文章

为啥不将哈希函数迭代 10,000,000 次? [复制]

python3的sympy

在 sympy 中评估系数而不是指数

为啥 RestTemplate 不将响应表示绑定到 PagedResources?

为啥 StandardScaler 不将元数据附加到输出列?

为啥不将 Javascript 事件委托发挥到极致呢?