两者可以输入向量值变量、矩阵值变量和符号函数,不可以直接输入匿名函数,需要使用sym转化为符号函数之后方可。
实际上gradient才是求导函数,原理是差分规则;而diff仅仅是一个求相邻两个元素之间的差的函数,其对应英文单词difference,虽然他好像也有“差分”的意思,但我总觉得此“差分”非彼差分,根本不是一个求导函数,在英文中经常说 "the difference between a and b" 来描述 a−b。
废话不多说,直接上栗子 1、对向量值变量求导
f = [1, 2, 3, 5, 8]
x = [1, 2, 4, 7, 11]
grad_f = gradient(f, x)
% 结果为
% grad_f =
% 1.0000 0.6667 0.6000 0.7143 0.7500
其原理为左右两个节点分别使用向前差分和向后差分,内部节点使用三点中心差分,计算之后的结果与f有相同的分量个数,即
grad_f(1) = [f(2)-f(1)] / [x(2)-x(1)] = 1 / 1
grad_f(2) = [f(3)-f(1)] / [x(3)-x(1)] = 2 / 3
grad_f(3) = [f(4)-f(2)] / [x(4)-x(2)] = 3 / 5
grad_f(4) = [f(5)-f(3)] / [x(5)-x(3)] = 5 / 7
grad_f(5) = [f(5)-f(4)] / [x(5)-x(4)] = 3 / 4
当x不显式指定是,计算式默认第一个和最后一个的分母为1,其余分母为2.
f = [1, 2, 3, 5, 8]
diff_f = diff(f, 1)
% 结果为
% diff_f =
% 1 1 2 3
diff不支持直接指定x,即相邻点之间的坐标,调用diff时输入的第二个变量1指的是对f求一次导数,实际上就是在计算相邻函数值的差值,因此结果会比原来的分量数少一个,如果第二个输入指定为2,则表示对f求两次导数,即对[1 1 2 3]再做一次导数,因此结果只有三个分量,即[0 1 1]。
这里 diff_f(1) = [f(2)-f(1)] = 1 diff_f(2) = [f(3)-f(2)] = 1 diff_f(3) = [f(4)-f(3)] = 2 diff_f(4) = [f(5)-f(4)] = 3 2、对矩阵值变量求导
f = magic(5);
x = linspace(0,5,5);
y = linspace(0,5,5);
grad_f = gradient(f, [x,y])
% 结果为
% f =
% 17 24 1 8 15
% 23 5 7 14 16
% 4 6 13 20 22
% 10 12 19 21 3
% 11 18 25 2 9
% grad_fx =
% 7.0 -8.0 -8.0 7.0 7.0
% -18.0 -8.0 4.5 4.5 2.0
% 2.0 4.5 7.0 4.5 2.0
% 2.0 4.5 4.5 -8.0 -18.0
% 7.0 7.0 -8.0 -8.0 7.0
% grad_fy =
% 6.0 -19.0 6.0 6.0 1.0
% -6.5 -9.0 6.0 6.0 3.5
% -6.5 3.5 6.0 3.5 -6.5
% 3.5 6.0 6.0 -9.0 -6.5
% 1.0 6.0 6.0 -19.0 6.0
这里仅以魔术矩阵和均匀步长做一个示例
grad_fx(1,1) = [f(1,2)-f(1,1)] / [x(2)-x(1)] = 7 / 1
grad_fx(1,2) = [f(1,3)-f(1,1)] / [x(3)-x(1)] = -16 / 2
grad_fx(1,3) = [f(1,4)-f(1,2)] / [x(4)-x(2)] = -16 / 2
grad_fy(1,1) = [f(2,1)-f(1,1)] / [y(2)-y(1)] = 6 / 1
grad_fy(2,1) = [f(3,1)-f(1,1)] / [y(3)-y(1)] = -13 / 2
grad_fy(3,1) = [f(4,1)-f(2,1)] / [y(4)-y(2)] = -13 / 2
以此类推不再赘述。
由此可以看到gradient求导的原理完全就是在基于差分公式,其任意方向的步长都是固定死的,对应的网格都是均匀的一致网格,而且据我所知在非一致网格上,差分本身就无法使用(至少我不会用)。
f = magic(5);
x = linspace(0,5,5);
y = linspace(0,5,5);
diff_fx = diff(f,1,1)
diff_fy = diff(f,1,2)
% 结果为
% f =
% 17 24 1 8 15
% 23 5 7 14 16
% 4 6 13 20 22
% 10 12 19 21 3
% 11 18 25 2 9
% diff_fx =
% 6 -19 6 6 1
% -19 1 6 6 6
% 6 6 6 1 -19
% 1 6 6 -19 6
% diff_fy =
% 7 -23 7 7
% -18 2 7 2
% 2 7 7 2
% 2 7 2 -18
% 7 7 -23 7
那diff函数呢,可以看到就是简单的做差而已,结果分别是4*5矩阵和5*4矩阵。
这里多维情况调用diff需增加第三个输入来表示对哪个方向做差分。
3、对符号变量求导
% 定义符号变量
syms x y
% 定义符号函数 u(x,y)
u = x.^2*y.^3;
% 计算梯度
gradU = gradient(u, [x, y])
diffUx = diff(u, x)
diffUy = diff(u, y)
% 计算结果
% gradU =
% 2*x*y^3
% 3*x^2*y^2
% diffUx =
% 2*x*y^3
% diffUy =
% 3*x^2*y^2
第三种情况还是很容易的,但是文章开头说diff本身不是一个求导函数,这里为什么两种方法的结果又会一模一样呢,其实diff设计出来是用于matlab符号计算工具箱中求导的。
当
diff
和
gradient
两个函数都用于符号函数时,尽管它们都计算导数或偏导数,但它们的设计目的和内部实现可能会有一些差异。
让我们来深入了解这两个函数的工作原理和适用场景。
内部原理和实现
diff的原理(符号计算)
: 目的和设计 :
diff
是专门设计用于计算符号表达式的导数的。
它遵循微积分的严格规则,比如链式法则、乘积法则、和法则等。
内部实现 :当
diff
应用于符号函数时,MATLAB 的符号引擎会解析表达式并应用这些数学规则来精确计算导数。
例如,对于
x^2 * y^3
,
diff
根据幂规则和乘积规则计算每个变量的偏导数。
gradient的原理(符号计算)
: 目的和设计 :虽然
gradient
最初是设计用于估计数值数据的梯度,但在符号环境中,它同样可以计算一个多元符号函数的所有偏导数。
这使得它适用于计算多变量函数的梯度向量。
内部实现 :在符号计算中,
gradient
同样会解析符号表达式并应用微积分规则来计算每个自变量的偏导数,其方式与
diff
相似。
它将所有偏导数组成一个向量返回,提供了一种一次性获得所有偏导数的方便方式。
是否一样?
实际计算 :在计算过程中,尽管
diff
和
gradient
可能使用相同的微积分规则来计算导数,但它们的设计目的和返回结果的形式有所不同。
diff
更多地用于单一变量的导数计算,而
gradient
提供了一种方便的方式来同时获得多变量函数的所有偏导数。
函数接口和输出 :
diff
通常用于更精确的单个导数或高阶导数计算,可以更灵活地指定求导次数和变量。
gradient
则侧重于直接生成梯度向量,这在物理和工程应用中尤其有用,当需要快速获得场的梯度描述时。
其实对于前两种情况,一般的数值型变量f来说,diff也是可以求导的,只不过不饿能简单的只使用diff,而是要diff(f,1)/diff(x,1),但使用
diff
方法得到的导数只反映了每对相邻点之间的直接变化率,没有考虑到边界处的处理,可以尝试以下代码,总之两者的差距还是挺大的
f = [1, 2, 3, 5, 8]
x = [1, 2, 4, 7, 11]
grad_f = gradient(f, x)
diff_f = diff(f,1)./diff(x,1)
% 结果为
% grad_f =
% 1.0000 0.6667 0.6000 0.7143 0.7500
% ans =
% 1.0000 0.5000 0.6667 0.7500
