系数函数很慢

时间:2011-11-23 14:13:34

标签: performance wolfram-mathematica expand

请考虑:

Clear[x]
expr = Sum[x^i, {i, 15}]^30;

CoefficientList[expr, x]; // Timing
Coefficient[Expand@expr, x, 234]; // Timing
Coefficient[expr, x, 234]; // Timing
{0.047, Null}
{0.047, Null}
{4.93, Null}

帮助说明:

  无论expr是否以扩展形式明确给出,

Coefficient都有效。

为什么Coefficient在最后一种情况下需要这么慢?

4 个答案:

答案 0 :(得分:12)

这是一个可以让您的代码快速运行的黑客攻击,但我不保证它始终正常工作:

ClearAll[withFastCoefficient];
SetAttributes[withFastCoefficient, HoldFirst];
withFastCoefficient[code_] :=
   Block[{Binomial},
     Binomial[x_, y_] := 10 /; ! FreeQ[Stack[_][[-6]], Coefficient];
     code]

使用它,我们得到:

In[58]:= withFastCoefficient[Coefficient[expr,x,234]]//Timing
Out[58]= {0.172,3116518719381876183528738595379210}

我们的想法是,Coefficient在内部使用Binomial来估算术语数量,然后在术语数量少于{{1时扩展(调用Expand)您可以使用1000进行检查。当它不扩展时,它会计算大量由零支配的大系数列表的总和,对于一系列表达式,这显然比扩展慢。我所做的是愚弄Trace[..., TraceInternal->True]返回一个小数字(Binomial),但我也试图这样做只会影响{{1}内部调用的10 }:

Binomial

但我无法保证没有代码中其他位置Coefficient将被错误计算的示例。

修改

当然,一个更安全的替代方案是使用Villegas - Gayley技巧重新定义In[67]:= withFastCoefficient[f[Binomial[7,4]]Coefficient[expr,x,234]] Out[67]= 3116518719381876183528738595379210 f[35] ,扩展其中的表达式并再次调用它:

Binomial

编辑2

我的第一个建议有一个优点,我们定义了一个宏,它在本地修改了函数的属性,但缺点是它不安全。我的第二个建议是更安全,但全局修改Coefficient,因此它将始终展开,直到我们删除该定义。在Internal`InheritedBlock的帮助下,我们可以充分利用这两个世界,它创建了给定函数的本地副本。这是代码:

Unprotect[Coefficient];
Module[{inCoefficient},
   Coefficient[expr_, args__] :=
      Block[{inCoefficient = True},
         Coefficient[Expand[expr], args]] /; ! TrueQ[inCoefficient]
];
Protect[Coefficient];

用法类似于第一种情况:

Coefficient

ClearAll[withExpandingCoefficient]; SetAttributes[withExpandingCoefficient, HoldFirst]; withExpandingCoefficient[code_] := Module[{inCoefficient}, Internal`InheritedBlock[{Coefficient}, Unprotect[Coefficient]; Coefficient[expr_, args__] := Block[{inCoefficient = True}, Coefficient[Expand[expr], args]] /; ! TrueQ[inCoefficient]; Protect[Coefficient]; code ] ]; 功能不受影响:

In[92]:= withExpandingCoefficient[Coefficient[expr,x,234]]//Timing
Out[92]= {0.156,3116518719381876183528738595379210}

答案 1 :(得分:10)

Coefficient除非认为绝对有必要,否则不会扩展。这确实可以避免记忆爆炸。我相信自第3版以来就一直这样(我想我大约在1995年左右开始研究它)。

避免扩张也可以更快。这是一个简单的例子。

In[28]:= expr = Sum[x^i + y^j + z^k, {i, 15}, {j, 10}, {k, 20}]^20;

In[29]:= Coefficient[expr, x, 234]; // Timing

Out[29]= {0.81, Null}

但是下一个似乎在版本8中挂起,并且在开发Mathematica(其中Expand已更改)中至少需要半分钟。

Coefficient[Expand[expr], x, 234]; // Timing

可能应该添加一些启发式方法来寻找不会爆炸的单变量。虽然看起来不是一个高优先级的项目。

Daniel Lichtblau

答案 2 :(得分:9)

expr = Sum[x^i, {i, 15}]^30; 

scoeff[ex_, var_, n_] /; PolynomialQ[ex, var] := 
   ex + O[var]^(n + 1) /. 
    Verbatim[SeriesData][_, 0, c_List, nmin_, nmax_, 1] :> 
     If[nmax - nmin != Length[c], 0, c[[-1]]]; 

Timing[scoeff[expr, x, 234]]

似乎也很快。

答案 3 :(得分:1)

在Rolf Mertig的回答之后经过一些实验后,这似乎是表达类型中最快的方法,例如Sum[x^i, {i, 15}]^30

SeriesCoefficient[expr, {x, 0, 234}]