我有一个Mathematica代码,我必须在数字上评估数千个与此类似的积分
NIntegrate[
(Pi*Cos[(Pi*(-2*x + y))/(1 + y)] + (1 + y)*(-Sin[(2*Pi*x)/(1 + y)] +
Sin[(Pi*(-2*x + y))/(1 + y)]))/(E^x*(1 + y)),
{x, 0, 100}, {y, 0, 100}
] //AbsoluteTiming
被积函数是一个很好的绝对可积的函数,没有奇点,它在一个方向上呈指数衰减,在另一个方向上以1 / y ^ 3衰减。
NIntegrate
命令在Mathematica 7中运行正常,但在最新版本8.0.4中,它减慢了两个数量级。我假设在新版本中它试图更好地控制错误,但代价是时间的巨大增加。是否有一些我可以使用的设置,以便计算以与Mathematica 7相同的速度进行?
答案 0 :(得分:16)
ruebenko 的回答以及来自 user1091201 和 Leonid 的评论结合在一起,可以给出正确答案。
ruebenko 的编辑1 回答是常规这类情况的正确答案,即添加选项{{1 }}:
Method -> {"SymbolicPreprocessing", "OscillatorySelection" -> False}
并且 user1091201 的评论建议expr = (Pi*
Cos[(Pi*(-2*x + y))/(1 + y)] + (1 + y)*(-Sin[(2*Pi*x)/(1 + y)] +
Sin[(Pi*(-2*x + y))/(1 + y)]))/(E^x*(1 + y));
NIntegrate[expr, {x, 0, 100}, {y, 0, 100},
Method -> {"SymbolicPreprocessing",
"OscillatorySelection" -> False}] // AbsoluteTiming
接近此特定问题的最快答案。
我将在这个具体示例中描述NIntegrate中发生的事情,并且一路上给出一些处理明显类似情况的提示。
方法选择
在这个例子中,NIntegrate检查Method -> "GaussKronrodRule"
,得出结论多维" LevinRule"是这个被积函数的最佳方法,并应用它。然而,对于这个特殊的例子," LevinRule"慢于" MultidimensionalRule" (尽管" LevinRule"得到更令人满意的误差估计)。 " LevinRule"也比在两个维度上迭代的几个高斯型一维规则中的任何一个慢,例如" GaussKronrodRule" user1091201 找到了。
NIntegrate在对被积函数进行一些符号分析后做出决定。应用了几种类型的符号预处理;设置expr
禁用一种符号预处理。
基本上,使用" OscillatorySelection"启用后,NIntegrate选择" LevinRule"。用" OscillatorySelection" NIntegrate选择" MultidimensionalRule",这对于这个积分更快,尽管我们可能会根据消息NIntegrate :: slwcon不信任结果,这表示观察到异常缓慢的收敛。
这是Mathematica 8与Mathematica 7不同的部分:Mathematica 8增加了" LevinRule"和相关的方法选择试探法" OscillatorySelection"。
除了使NIntegrate选择不同的方法之外,禁用" OscillatorySelection"还节省了进行实际符号处理所花费的时间,这在某些情况下可能很重要。
设置Method -> {"SymbolicPreprocessing", "OscillatorySelection" -> False}
覆盖并跳过与方法选择关联的符号处理,而是使用二维笛卡尔积规则Method -> "GaussKronrodRule"
。这恰好是这种积分的一种非常快速的方法。
其他符号处理
ruebenko 的Method -> {"CartesianRule", Method -> {"GaussKronrodRule", "GaussKronrodRule"}}
和 user1091201 ' Method -> {"SymbolicPreprocessing", "OscillatorySelection" -> False}
不会禁用其他形式的符号处理,这通常是件好事。有关可能应用的符号预处理类型的列表,请参阅this part of the NIntegrate advanced documentation。特别是" SymbolicPiecewiseSubdivision"由于存在分段函数,对于在几个点处非分析的积分非常有价值。
要禁用所有符号处理并仅使用默认方法选项获取默认方法,请使用Method -> "GaussKronrodRule"
。对于一维积分,这当前等于Method -> {Automatic, "SymbolicProcessing" -> 0}
,具有这些方法的所有参数的某些默认设置(规则中的插值点的数量,全局自适应策略的奇点处理的类型等)。对于多维积分,它当前等于Method -> {"GlobalAdaptive", Method -> "GaussKronrodRule"}
,同样具有某些默认参数值。对于高维积分,将使用蒙特卡罗方法。
我不建议直接切换到Method -> {"GlobalAdaptive", Method -> "MultidimensionalRule"}
作为NIntegrate的第一个优化步骤,但在某些情况下它可能很有用。
最快的方法
只有总是某种方式可以加速数值积分至少一点,有时候很多,因为有很多参数可以启发式选择,你可以从调整中受益。 (查看"LevinRule" method或"GlobalAdaptive" strategy具有的不同选项和参数,包括所有子方法等。)
那就是说,这是我找到的最快的方法:
Method -> {Automatic, "SymbolicProcessing" -> 0}
(设置NIntegrate[(Pi*
Cos[(Pi*(-2*x + y))/(1 + y)] + (1 + y)*(-Sin[(2*Pi*x)/(1 + y)] +
Sin[(Pi*(-2*x + y))/(1 + y)]))/(E^x*(1 + y)), {x, 0,
100}, {y, 0, 100},
Method -> {"GlobalAdaptive", Method -> "GaussKronrodRule",
"SingularityDepth" -> Infinity}] // AbsoluteTiming
禁用奇点处理转换。)
整合范围
顺便提一下,您所需的集成范围是"SingularityDepth" -> Infinity
,还是{x, 0, 100}, {y, 0, 100}
是您应用的真正所需集成范围?
如果你真的需要{x, 0, Infinity}, {y, 0, Infinity}
,我建议改用它。对于无限长度积分范围,NIntegrate将被积函数压缩到有限范围,有效地以几何间隔方式对其进行采样。这通常比用于有限积分范围的线性(均匀)间隔样本更有效。
答案 1 :(得分:8)
这是一种解决方法:
NIntegrate[(Pi*
Cos[(Pi*(-2*x + y))/(1 + y)] + (1 + y)*(-Sin[(2*Pi*x)/(1 + y)] +
Sin[(Pi*(-2*x + y))/(1 + y)]))/(E^x*(1 + y)), {x, 0,
100}, {y, 0, 100},
Method -> "AdaptiveMonteCarlo"] // AbsoluteTiming
您也可以使用ParallelTry并行测试各种方法。
在实施新方法或修改启发式方法时,可能会发生特定参数的减速。这些可能有助于解决一类新问题,而其他一些问题则变得更慢。人们必须仔细调查这里发生了什么。
您可能想要更改问题的主题 - 它表示所有积分在V8中评估较慢,这是不正确的。
修改1 : 我认为它被卡在LevinRule中(V8中用于振荡积分的新东西)所以,我认为,这应该关闭它。
NIntegrate[(Pi*
Cos[(Pi*(-2*x + y))/(1 + y)] + (1 + y)*(-Sin[(2*Pi*x)/(1 + y)] +
Sin[(Pi*(-2*x + y))/(1 + y)]))/(E^x*(1 + y)), {x, 0,
100}, {y, 0, 100},
Method -> {"SymbolicPreprocessing",
"OscillatorySelection" -> False}] // AbsoluteTiming
答案 2 :(得分:2)
对于这个特殊积分,主要罪魁祸首似乎是x
上的积分,可能是由于存在快速衰减和高振荡项。此外,对于这种情况,可以通过分析x
进行集成:
In[92]:=
-Integrate[(Pi*Cos[(Pi*(-2*x+y))/(1+y)]+(1+y)*(-Sin[(2*Pi*x)/(1+y)]+Sin[(Pi*(-2*x+y))/(1+y)]))/
(E^x* (1+y)),x]/.x->0//FullSimplify
Out[92]= (-\[Pi] (1+y) (2+Cos[(\[Pi] y)/(1+y)])+(2 \[Pi]^2+(1+y)^2) Sin[(\[Pi] y)/(1+y)])/
(4 \[Pi]^2+(1+y)^2)
(我放弃了上限的值,因为它y
一致非常小)。然后可以用y
数字积分来获得正确的结果:
In[94]:= NIntegrate[%,{y,0,100}]
Out[94]= 1.17525
更一般的解决方法是将x
和y
集成分开,如下所示:
ClearAll[f];
f[y_?NumericQ, xrange : {_?NumericQ, _?NumericQ}] :=
NIntegrate[(Pi*
Cos[(Pi*(-2*x + y))/(1 + y)] + (1 +
y)*(-Sin[(2*Pi*x)/(1 + y)] +
Sin[(Pi*(-2*x + y))/(1 + y)]))/(E^x*(1 + y)), {x, First@xrange, Last@xrange}];
然后我们有:
In[105]:= NIntegrate[f[y,{0,100}],{y,0,100}]//Timing
Out[105]= {2.578,1.17525}
这不是很快,但速度相当快。这个过程并不总是能很好地工作(因为这个过程产生的2D集成网格并不总是最优),但是当被积函数高于x
和{{1}时的积分时,它应该运行得很好充分“解耦”。