避免重复调用插值函数

8

我希望在Mathematica中插值一个函数。

该函数依赖于参数a,实际上它是一个反函数F的倒数,该函数也依赖于a,因此我按照以下方式构建近似:

approx = Interpolation[Table[{F[0.1 n, a], 0.1 n}, {n, -100, 100}]]

现在我只需调用approx[x]即可在特定点处求逆函数。

相反,我想做这样的事情:定义一个带参数的函数,

G[x_,a_] = "construct the interpolating function,
            and return the value of the function at x"

那么写G[x,a]来评估该函数。否则,我将不得不针对我感兴趣的所有参数重复插值,并且有很多变量悬而未决。我尝试将Interpolation[]调用放在模块中,但这只会在每次调用G[x,a]时构建插值!我该如何避免这种情况?

谢谢阅读。

3个回答

12

第一步是使用aapprox进行参数化:

approx[a_] := Interpolation[Table[{F[0.1 n,a],0.1 n},{n,-100,100}]]

有了这个定义,G就可以这样定义:

G[x_, a_] := approx[a][x]

但是,正如问题中观察到的那样,这将导致每次调用G时都重建插值。避免这种情况的一种方法是使用备忘录重新定义approx

m: approx[a_] := m = Interpolation[Table[{F[0.1 n,a],0.1 n},{n,-100,100}]]

现在,approx将保存任何给定的a的插值函数,避免在后续调用中针对相同的a进行重建。当然,这会使用内存,因此如果有大量不同的a值,则内存可能会不足。可以通过将保存的值与另一个符号(在本例中为cache)相关联来本地化approx使用的缓存:

approx[a_] := cache[a] /.
  _cache :> (cache[a] = Interpolation[Table[{F[0.1` n,a],0.1` n},{n,-100,100}]])

使用这个版本的 approx,可以通过 Block 来本地化 cache,例如:

Block[{cache}
, Table[G[x, a], {x, 0, 5}, {a, 0, 1, 0.1}]
]

插值函数仍然暂时存储在每个不同的a值中,但现在这些保存的定义在Block退出后被释放。

关于Mathematica中具有内存功能的函数的更多信息,请参阅以下SO问题:

构造带有记忆的函数的最佳方式

Mathematica中的动态规划:如何自动局部化和/或清除记忆化函数的定义


谢谢您的答复,它激励我去学习更多的Mathematica! - mark

6
尝试以下内容:

试着按照以下方式做:

G[a_]:=G[a]=Interpolation[Table[{F[0.1 n, a], 0.1 n}, {n, -100, 100}]]

G[0.2]  (* particular value of G[a] *)

G[0.2][0.3] (* the value you want *)

你只有在每次使用特定的a值时第一次调用G时才会计算它。

这种方法似乎是提供的其他方法中最简单的方法。也许与其他答案中描述的显式缓存相比,这种方案存在一些缺点。但我不够专业,不知道区别在哪里。 - mark
3
其他回答介绍了如何释放缓存符号中存储的内存,当其增长过大时。这些缓存的想法与此答案相同。如果您的代码不太占用内存,则不必真正关心释放当前会话的内存,但在进行大型计算时释放内存可能很有用。 - faysou

6
你可以使用我在“你的Mathematica工具包中有什么?”中发布的CacheIndex定义。使用这个函数的一个好处是,您可以缓存值或代码部分,而无需定义新函数(尽管我们在此处这样做以跟随示例)。
G[x_,a_] :=
   CacheIndex[a,
      Pause[3];
      Interpolation[Table[{F[0.1 n,a],0.1 n},{n,-100,100}]]
   ][x];

我加入了 Pause[3] 只是为了清楚地表明 Interpolation 的定义被缓存,每当它被计算一次后会对 a 进行缓存。您可以使用下面的代码从 CacheIndex 中删除已缓存的 Interpolation 值:
DeleteCachedValues[CacheIndex] (*or*) 
DeleteCachedValues[CacheIndex,1].

我已经修改了我的Cache和CacheIndex函数,使它们与WReach的想法兼容,即使用在Block中定义的单独符号。这里有一个不太实用的问题,就是你必须为用作缓存的符号定义Hold属性,但这个想法仍然很有趣。

下面是CacheSymbol的定义:

SetAttributes[CacheSymbol,HoldAll];
CacheSymbol[cacheSymbol_,expr_]:=cacheSymbol[expr]/.(_cacheSymbol:>(cacheSymbol[expr]=expr));

您可以按照以下说明测试此实现,在真实示例中,cache将在块中定义。
ClearAll[cache]
SetAttributes[cache,HoldFirst] 
CacheSymbol[cache,Pause[3];2+2]
?cache
CacheSymbol[cache,Pause[3];2+2]

这里是 CacheSymbolIndex 的定义。
SetAttributes[CacheIndexSymbol,HoldAll];
CacheIndexSymbol[cacheSymbol_,index_,expr_]:=cacheSymbol[index,expr]/.(_cacheSymbol:>(cacheSymbol[index,expr]=expr));

您可以按照以下说明测试此实现,在真实的示例中,缓存将在块中定义。
ClearAll[cache] 
SetAttributes[cache,HoldRest]
CacheIndexSymbol[cache,2+2,Pause[3];2+2]
?cache
CacheIndexSymbol[cache,2+2,Pause[3];2+2]

类似于WReach的示例,我们也会有:

G[x_,a_] :=
   CacheIndexSymbol[cache,a,
      Print["Caching"];
      Interpolation[Table[{F[0.1 n,a],0.1 n},{n,-100,100}]]
   ][x]

Block[{cache}, 
   SetAttributes[cache,HoldRest];
   Table[G[x, a], {x, 0, 5}, {a, 0, 1, 0.1}]
]

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