请考虑:
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
在最后一种情况下需要这么慢?答案 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}]