【发布时间】:2022-01-13 12:51:00
【问题描述】:
我有以下问题。我使用 numpy 函数在 python 中定义了一个函数f。该函数在正实数上是平滑且可积的。我想构造函数的双反导数(假设反导数在 0 处的值和斜率均为 0),以便我可以在任何小于 100 的正实数上对其进行评估。
f 的反导数在x 的定义:
integrate f(s) with s from 0 to x
fx 的双反导数的定义:
integrate (integrate f(t) with t from 0 to s) with s from 0 to x
f 的实际形式并不重要,所以为了方便,我将使用一个简单的形式。但请注意,即使我的示例具有已知的封闭形式,但我的实际函数却没有。
import numpy as np
f = lambda x: np.exp(-x)*x
我的解决方案是使用简单的数值积分将反导数构造为数组:
N = 10000
delta = 100/N
xs = np.linspace(0,100,N+1)
vs = f(xs)
avs = np.cumsum(vs)*delta
aavs = np.cumsum(avs)*delta
这当然有效,但它给了我数组而不是函数。但这不是一个大问题,因为我可以使用样条线插入 aavs 以获得函数并摆脱数组。
from scipy.interpolate import UnivariateSpline
aaf = UnivariateSpline(xs, aavs)
函数aaf 近似为f 的双重反导数。
问题是即使它有效,在我获得我的功能之前还有相当多的开销,而且精度很昂贵。
我的另一个想法是通过样条插值f 并取其反导数,但这会引入对于我想要使用该函数来说太大的数值错误。
有没有更好的方法来做到这一点?更好是指在不牺牲准确性的情况下更快。
编辑:我希望可以使用某种傅里叶变换来避免积分两次。我希望vs 有一些方便的转换,允许将值与xs 逐个相乘并转换回来以获得双重反导数。我玩了一下这个,但我迷路了。
编辑:我发现通过使用梯形规则而不是简单求和,可以大大提高准确性。使用 Simpson 规则应该会进一步提高准确度,但是对于 numpy 数组来说有点繁琐。
编辑:正如@user202729 理所当然地抱怨的那样,这似乎不对。它似乎关闭的原因是因为我跳过了一些细节。我在这里解释了为什么我说的有道理,但这并不影响我的问题。
我的实际目标不是找到f 的双重反导数,而是找到它的变换。我已经跳过了,因为我认为它只会混淆问题。
函数f 在 x 接近 0 或无穷大时呈指数衰减。我通过从 0 开始求和到大约 f 的峰值来最小化积分中的数值误差。这确保了相对误差大致恒定。然后我从一些非常大的 x 的相反方向开始,然后回到峰值。然后我对反微分值做同样的事情。
然后我通过另一个对数值错误敏感的函数来转换aavs。然后我找到错误很大的区域(值剧烈振荡)并删除这些值。最后,我用样条线近似我认为是好的值。
现在,如果我使用样条逼近f,它会引入一个绝对误差,这是一个相当大的区间内的主导项。这被“集成”了两次,最终在aavs 中成为一个相当大的相对错误。然后一旦我改造aavs,我发现'好区域'已经大大缩小了。
编辑:f 的实际形式是我仍在研究的东西。但是,它将是对数正态分布的推广。现在我正在和下面的家人一起玩。
我首先定义正态分布的概括:
def pdf_n(params, center=0.0, slope=8):
scale, min, diff = params
if diff > 0:
r = min
l = min + diff
else:
r = min - diff
l = min
def retfun(m):
x = (m - center)/scale
E = special.expit(slope*x)*(r - l) + l
return np.exp( -np.power(1 + x*x, E)/2 )
return np.vectorize(retfun)
这里发生的事情可能并不明显,但结果很简单。该函数在左侧衰减为 exp(-x^(2l)),在右侧衰减为 exp(-x^(2r))。对于 min=1 和 diff=0,这是正态分布。请注意,这不是标准化的。然后我定义
g = pdf(params)
f = np.vectorize(lambda x:g(np.log(x))/x/area)
其中area 是归一化常数。
请注意,这不是我使用的实际代码。我将其剥离到最低限度。
【问题讨论】:
-
你看过 scipy 数值积分方法吗? docs.scipy.org/doc/scipy/reference/tutorial/integrate.html
-
您知道您的目标是什么级别的准确度和延迟/计算复杂性吗?
-
数值积分方法返回定积分,即特定区间的值。类似结果的函数需要多次调用。另一种方法可能是使用
sympy.integrate来生成可以是lambdified的表达式。 (例如(x + 2)*exp(-x)) -
我肯定会使用Laplaces Method 集成技巧。你有
exp( hard stuff ),这正是这个技巧的设计目的。但是 - 我不知道是否有更通用的版本允许最终结果的任意最大误差。好处是您可以手动完成,与计算机速度无关。 (计算技巧不会让你找到O(1)) -
啊哈-你可以keep higher order terms with Laplaces Method。这既可以让您获得所需的最大误差,又可以让您获得第二个积分。你需要一段时间才能弄清楚数学。我建议 sympy 作为数学工具。一旦您可以打印出近似值的封闭形式,您就应该准备好进行几乎即时的计算了。
标签: python numpy numerical-integration