我正在寻找一种使用离散和快速方法计算导数的方法。因为现在我不知道我所拥有的方程的类型,所以我正在寻找类似于我们可以找到的积分方法的离散方法,例如欧拉方法。
10 回答
我认为您正在寻找以点计算的导数。如果是这种情况,这里有一个简单的方法可以做到这一点。你需要知道一点的导数,比如a。它由 h->0 的差商的极限给出:
您实际上需要实现限制功能。那么你:
- 定义一个 epsilon,设置得越小越精确,越大越快
- 计算起始h的差商,假设h=0.01,存入f1
现在在 DO-WHILE 循环中:
1- 将 h 除以 2(或除以 10,重要的是使其更小)
2- 再次计算与 h 的新值的差商,并将其存储在f2
3- 设置diff = abs(f2-f1)
4 - 分配f1 = f2
5- 从点 1 重复while (diff>epsilon)- 您最终可以将 f1(或 f2)作为 f'(a) 的值返回
请记住:您假设函数在 a 中是可微的。由于您的计算机可以处理的有限十进制数字的错误,您将得到的每个结果都是错误的,这是无法逃脱的。
python中的示例:
def derive(f, a, h=0.01, epsilon = 1e-7):
f1 = (f(a+h)-f(a))/h
while True: # DO-WHILE
h /= 2.
f2 = (f(a+h)-f(a))/h
diff = abs(f2-f1)
f1 = f2
if diff<epsilon: break
return f2
print "derivatives in x=0"
print "x^2: \t\t %.6f" % derive(lambda x: x**2,0)
print "x:\t\t %.6f" % derive(lambda x: x,0)
print "(x-1)^2:\t %.6f" % derive(lambda x: (x-1)**2,0)
print "\n\nReal values:"
print derive(lambda x: x**2,0)
print derive(lambda x: x,0)
print derive(lambda x: (x-1)**2,0)
输出:
derivatives in x=0
x^2: 0.000000
x: 1.000000
(x-1)^2: -2.000000
Real values:
7.62939453125e-08
1.0
-1.99999992328
由于只使用结果的前 6 位,我第一次得到“精确”值”,注意我使用 1e-7 作为 epsilon。之后打印了 REAL 计算值,它们显然在数学上是错误的。选择多小的 epsilon 取决于您希望结果有多精确。
在计算数值(“有限”)导数方面有相当多的理论(和既定实践)。让所有细节正确,让您相信结果,这并非易事。如果有任何方法可以获得函数的解析导数(使用笔和纸,或计算机代数系统,如Maple、Mathematica、Sage或SymPy),这是迄今为止最好的选择。
如果你不能得到解析形式,或者你不知道函数(只是它的输出),那么数值估计是你唯一的选择。C 语言中的数值方法的这一章是一个好的开始。
一个简单的方法是计算您感兴趣的导数的每个点的 f 在一个小值上的变化。例如,要计算 ∂f/∂x,您可以使用以下命令:
epsilon = 1e-8
∂f/∂x(x, y, z) = (f(x+epsilon,y,z) - f(x-epsilon, y, z))/(epsilon * 2);
其他部分在 y 和 z 中是相似的。
为 epsilon 选择的值取决于 f 的内容、所需的精度、使用的浮点类型以及可能的其他因素。我建议你用你感兴趣的函数来试验它的值。
要进行数值微分(始终是近似值),有两种常见情况:
- 您有一种方法(算法、方程)来计算任何给定 x 处的 f(x) 的值,或者
- 您在一组等距的 x 值(f(1)、f(1.5)、f(2) 等)处获得 f(x) 的值:
- 如果你有算法,那么你最好看看Andrea Ambu 的答案*:
- 从 f(a+h) 和 f(ah) 的值开始,然后执行
f'(a) = { f(a+h)-f(ah) } / 2h
这称为中心差。 - 根据 Andrea 的回答,减小偏移量 h 的大小,直到 f'(a) 停止变化。
- 请注意不要在不检查它是否大于 0 的情况下除以 2h,否则会出现除以零错误。
- 从 f(a+h) 和 f(ah) 的值开始,然后执行
- 如果你有一组 f(x) = f(x n ) = f n的值的函数值,那么我们可以做类似上述方法的事情,但我们的准确性会受到 x 值的限制n以及它们之间的差距。
- 第一个近似只是使用与上面相同的中心差分算子;
f' n = (f n+1 - f n-1 )/2h,
其中 h 是 x n值之间的相等间距。 - 接下来是使用五点模板,尽管它只有 4 个系数,如该维基百科页面上所述。这使用从 f n-2到 f n+2的值:f' n = (-f n+2 + 8f n+1 - 8f n-1 +f n-2 )/12h。
- 有使用更多点的更高阶近似值,但它们越来越难以计算收益递减,并且在数值上变得更加不稳定。
- **注意**:这些有限差分公式依赖于 f 与 x n-1 ≤ x ≤ n+1范围内的多项式的形状大致相同。对于像正弦波这样的函数,它们在计算导数方面可能非常糟糕,而且精度很高。
* Andrea 的答案使用前向差分算子 {f(a+h) - f(a)}/h 而不是中心差分算子 {f(a+h) - f(ah)}/2h,但是前向差分算子在数值解中不太准确。
如果不使用 Maple 之类的符号数学语言,您能做的最好的事情就是在各个点近似导数。(然后插值,如果你需要一个函数。)
如果您已经获得了要使用的功能,那么您应该使用后向除差论坛la和Richardson 外推来改善您的错误。
还要记住,这些方法适用于一个变量的函数。但是,每个变量的偏导数将其他变量视为常数。
自动微分是做这种事情的最准确和概念上最棒的方式。只是稍微复杂一点。
正式地,没有。您正在描述离散函数的(偏)导数,或者您正在要求一种数值方法来近似连续函数的(偏)导数。
离散函数没有导数。如果您查看导数的 epsilon-delta 定义,您会发现您需要能够在接近您想要导数的点处评估函数。如果函数的值只有 x、y 和 z 的整数值,这没有任何意义。所以没有办法找到任何快速值的离散函数的导数。
如果您想要一种数值方法来精确计算连续函数的导数,那么您也不走运。导数的数值方法是启发式的,而不是算法的。没有数值方法可以保证精确解。幸运的是,存在许多好的启发式方法。Mathematica默认使用Brent 主轴方法的专用版本。我建议你使用GNU Scientific Library,它很好地实现了 Brent 的方法。我的一门数学课程的全部成绩都归功于 GSL。如果这是你的事,红宝石绑定非常好。如有必要,大多数数值微分库都有一些可用的不同方法。
如果你真的想要,我可以拿出一些示例代码。让我知道。
我假设您的函数比您发布的简单函数更复杂,因为封闭形式的解决方案太简单了。
当您使用“离散”一词时,我认为您需要“有限差异”。您需要一些离散化来计算近似值。
Df/Dx ~ (f2-f1)/(x2-x1)等
我希望这可能对数值微分有所帮助。
如果函数如您所指出的那样是线性的,那么导数是微不足道的。关于“x”的导数是“a”;关于“y”的导数是“b”,关于“z”的导数是“c”。如果方程是更复杂的形式,并且您需要一个表示解的公式而不是经验解,那么请提交更复杂的方程形式。
问候