0.前言
相信各位研友在上张宇的课时,会听到宇神提到的分数阶求导。还记得当时在复习导数,一个『分数阶求导』的词不经意间跑过,但却把我的兴趣给抓牢了。
现在,我就来系统地叨叨分数阶求导的知识,也当是给各位消遣时的学习了。如有缺漏,还请指正,谢谢!
1. 什么是分数阶导数?
分数阶导数的通俗解释是,它是一种介于微分和积分之间的运算,它可以用来描述一些既不是局部的也不是全局的,而是介于两者之间的现象。
例如,如果一个物体的位移与时间的关系是 x ( t ) = t α x(t)=t^\alpha x(t)=tα,那么它的速度就是 v ( t ) = α t α − 1 v(t)=\alpha t^{\alpha-1} v(t)=αtα−1,它的加速度就是 a ( t ) = α ( α − 1 ) t α − 2 a(t)=\alpha(\alpha-1)t^{\alpha-2} a(t)=α(α−1)tα−2。如果 α \alpha α 是一个整数,那么这些都是标准的导数。但是如果 α \alpha α 是一个分数,那么这些可以被理解为分数阶导数,但并没有那么简单。
分数阶导数可以用来描述一些具有非线性或非平稳的特征的运动,例如分数阶谐振子,分数阶阻尼振荡器,分数阶布朗运动等。
分数阶导数有多种不同的定义,其中最常用的是 R i e m a n n − L i o u v i l l e Riemann-Liouville Riemann−Liouville 分数阶导数和 C a p u t o Caputo Caputo 分数阶导数1:
D α f ( x ) = 1 Γ ( n − α ) d n d x n ∫ 0 x f ( t ) ( x − t ) α − n + 1 d t , n − 1 < α < n D^{\alpha}f(x) = \frac{1}{\Gamma(n-\alpha)}\frac{d^n}{dx^n}\int_0^x \frac{f(t)}{(x-t)^{\alpha-n+1}}dt, \quad n-1<\alpha<n Dαf(x)=Γ(n−α)1dxndn∫0x(x−t)α−n+1f(t)dt,n−1<α<n
a D x α f ( x ) = 1 Γ ( n − α ) ∫ a x f ( n ) ( t ) ( x − t ) α − n + 1 d t , n − 1 < α < n {}_{a}D^{\alpha}_x f(x) = \frac{1}{\Gamma(n-\alpha)}\int_a^x \frac{f^{(n)}(t)}{(x-t)^{\alpha-n+1}}dt, \quad n-1<\alpha<n aDxαf(x)=Γ(n−α)1∫ax(x−t)α−n+1f(n)(t)dt,n−1<α<n
其中 α \alpha α 是任意正实数, Γ \Gamma Γ 是 G a m m a Gamma Gamma 函数, n n n 是大于 α \alpha α 的最小整数, f ( n ) f^{(n)} f(n) 是 f f f 的 n n n 阶导数。这两种定义的区别在于 R i e m a n n − L i o u v i l l e Riemann-Liouville Riemann−Liouville 分数阶导数是对原函数 f f f 进行分数阶积分,然后再求整数阶导数,而 C a p u t o Caputo Caputo 分数阶导数是是对 f f f 的最高整数阶导数积分,即,先对原函数 f f f 求整数阶导数,然后再进行分数阶积分。 C a p u t o Caputo Caputo 分数阶导数的优点是它可以保持原函数的初值条件,而 R i e m a n n − L i o u v i l l e Riemann-Liouville Riemann−Liouville 分数阶导数则需要引入分数阶初值条件2。
2. 怎样理解分数阶导数?
相信看到这一大串公式以后,大家都会比较茫然,不知道该怎样理解。我们可以从它的最基本的物理意义和几何意义着手进行理解。
2.1 物理意义
所以,请让我们回顾一下整数阶导数的定义,它是通过极限的概念来定义的,例如一阶导数的定义是:
f ′ ( x ) = lim h → 0 f ( x + h ) − f ( x ) h , f'(x) = \lim_{h \to 0} \frac{f(x+h)-f(x)}{h}, f′(x)=h→0limhf(x+h)−f(x),
它表示函数 f f f 在点 x x x 处的变化率,也就是切线的斜率。如果 f f f 表示位移,那么 f ′ f' f′ 就表示速度。二阶导数的定义是:
f ′ ′ ( x ) = lim h → 0 f ′ ( x + h ) − f ′ ( x ) h , f''(x) = \lim_{h \to 0} \frac{f'(x+h)-f'(x)}{h}, f′′(x)=h→0limhf′(x+h)−f′(x),
它表示函数 f ′ f' f′ 在点 x x x 处的变化率。如果 f ′ f' f′ 表示速度,那么 f ′ ′ f'' f′′ 就表示加速度。这些都是整数阶导数的情况,它们都是基于极限的概念,也就是说,它们只考虑了函数在某一点附近的局部性质,而忽略了函数在其他地方的全局性质3。这样的导数可以用来描述一些简单的运动,例如匀速或匀加速运动,但是对于一些更复杂的运动,例如非线性或非平稳的运动,这样的导数就不够用了。
分数阶导数的定义是通过积分的概念来定义的,它是通过对函数进行多次积分,然后再求导的方式来得到的,例如 R i e m a n n − L i o u v i l l e Riemann-Liouville Riemann−Liouville 分数阶导数的定义是:
D α f ( x ) = 1 Γ ( n − α ) d n d x n ∫ 0 x f ( t ) ( x − t ) α − n + 1 d t , n − 1 < α < n , D^\alpha f(x) = \frac{1}{\Gamma(n-\alpha)}\frac{d^n}{dx^n}\int_0^x \frac{f(t)}{(x-t)^{\alpha-n+1}}dt, \quad n-1<\alpha<n, Dαf(x)=Γ(n−α)1dxndn∫0x(x−t)α−n+1f(t)dt,n−1<α<n,
它表示函数 f f f 在点 x x x 处的 α \alpha α 阶导数,其中 α \alpha α 是任意正实数, n n n 是大于 α \alpha α 的最小整数, Γ \Gamma Γ 是 G a m m a Gamma Gamma 函数。
这个定义的含义是,先对函数 f f f 在区间 [ 0 , x ] [0,x] [0,x] 上进行 n − α n-\alpha n−α 次积分,得到一个新的函数 F F F,然后再对 F F F 在点 x x x 处求 n n n 阶导数,得到 D α f ( x ) D^\alpha f(x) Dαf(x)。这样的导数可以用来描述一些复杂的运动,例如具有记忆效应或历史依赖性的运动,因为它不仅考虑了函数在某一点的局部性质,还考虑了函数在其他地方的全局性质。这是因为积分的过程相当于对函数的历史进行了累积,而导数的过程相当于对函数的当前进行了微分。所以分数阶导数可以看作是一种结合了积分和导数的运算,它可以捕捉到函数的更多信息,而不是只关注函数的一阶或二阶变化。
2.2 几何意义
比如,对于整数阶导数的图形,它们都是与曲线的切线或曲率有关的。
例如一阶导数表示曲线 y = f ( x ) y=f(x) y=f(x) 在点 ( x , f ( x ) ) (x,f(x)) (x,f(x)) 处的切线的斜率,也就是曲线在该点的方向。如果曲线是直线,那么它的一阶导数就是常数,表示曲线的斜率不变。如果曲线是曲线,那么它的一阶导数就是变化的,表示曲线的斜率随着位置的变化而变化。
二阶导数的表示曲线
y
=
f
(
x
)
y=f(x)
y=f(x) 在点
(
x
,
f
(
x
)
)
(x,f(x))
(x,f(x)) 处的曲率,也就是曲线在该点的弯曲程度。如果曲线是直线,那么它的二阶导数就是零,表示曲线的曲率不存在。如果曲线是圆,那么它的二阶导数就是常数,表示曲线的曲率不变。如果曲线是曲线,那么它的二阶导数就是变化的,表示曲线的曲率随着位置的变化而变化。这些都是整数阶导数的情况,它们都是基于切线或曲率的概念,也就是说,它们只反映了曲线在某一点的局部性质,而忽略了曲线的整体形状。这样的导数可以用来描述一些简单的曲线,例如直线或圆,但是对于一些更复杂的曲线,例如分形或混沌,这样的导数就不够用了。
分数阶导数的图形是与曲线的整体形状有关的,它们不是切线或曲率,而是一种更抽象的概念,例如 R i e m a n n − L i o u v i l l e Riemann-Liouville Riemann−Liouville 分数阶导数的图形是:
它表示曲线 y = f ( x ) y=f(x) y=f(x) 在点 ( x , f ( x ) ) (x,f(x)) (x,f(x)) 处的 α \alpha α 阶导数,其中 α \alpha α 是任意正实数。
这个图形的含义是,先对曲线 y = f ( x ) y=f(x) y=f(x) 在区间 [ 0 , x ] [0,x] [0,x] 上进行 n − α n-\alpha n−α 次积分,得到一个新的曲线 y = F ( x ) y=F(x) y=F(x),然后再对 F F F 在点 ( x , F ( x ) ) (x,F(x)) (x,F(x)) 处求 n n n 阶导数,得到 D α f ( x ) D^\alpha f(x) Dαf(x)。这样的导数可以用来描述一些复杂的曲线,例如具有非整数维度或非马尔可夫性质的曲线,因为它不仅反映了曲线在某一点的局部性质,还反映了曲线的整体形状。这是因为积分的过程相当于对曲线的形状进行了平滑,而导数的过程相当于对曲线的形状进行了放大4。所以分数阶导数可以看作是一种结合了平滑和放大的运算,它可以反映出曲线的更多特征,而不是只关注曲线的一阶或二阶变化。
3. 计算方法
3.1 近似为有限差分
要计算分数阶导数,有多种数值方法,其中一种是基于 G r u n w a l d − L e t n i k o v Grunwald-Letnikov Grunwald−Letnikov 公式的方法,它可以将分数阶导数近似为有限差分的形式5:
D α f ( x ) ≈ 1 h α ∑ k = 0 N ( − 1 ) k ( α k ) f ( x − k h ) D^{\alpha}f(x) \approx \frac{1}{h^{\alpha}}\sum_{k=0}^N (-1)^k \binom{\alpha}{k} f(x-kh) Dαf(x)≈hα1k=0∑N(−1)k(kα)f(x−kh)
其中 h h h 是步长, N N N 是取决于 x x x 和 h h h 的整数, ( α k ) \binom{\alpha}{k} (kα) 是广义二项式系数,定义为:
( α k ) = α ( α − 1 ) ⋯ ( α − k + 1 ) k ! , k = 0 , 1 , 2 , … \binom{\alpha}{k} = \frac{\alpha(\alpha-1)\cdots(\alpha-k+1)}{k!}, \quad k=0,1,2,\dots (kα)=k!α(α−1)⋯(α−k+1),k=0,1,2,…
我用了Matlab和C++分别对这个公式进行了代码实现。以下是一个用 MATLAB 编写的函数,它可以计算任意函数 f f f 在任意点 x x x 处的分数阶导数:
function y = fgl_deriv(f, x, alpha)
% f: a function handle
% x: a scalar value
% alpha: a positive real number
% y: the fractional derivative of f at x of order alpha
h = sqrt(eps); % choose a small step size
N = floor(x / h); % choose the number of terms
y = 0; % initialize the output
for k = 0:N % loop over the summation
y = y + (-1)^k * nchoosek(alpha, k) * f(x - k * h); % add each term
end
y = y / h^alpha; % divide by the power of h
end
或者我们用C++来编写:
#include <cmath>
#include <functional>
double fgl_deriv(std::function<double(double)> f, double x, double alpha)
{
// f: a function object
// x: a scalar value
// alpha: a positive real number
// return: the fractional derivative of f at x of order alpha
double h = std::sqrt(std::numeric_limits<double>::epsilon()); // choose a small step size
int N = std::floor(x / h); // choose the number of terms
double y = 0; // initialize the output
for (int k = 0; k <= N; k++) // loop over the summation
{
y += std::pow(-1, k) * std::tgamma(alpha + 1) / (std::tgamma(k + 1) * std::tgamma(alpha - k + 1)) * f(x - k * h); // add each term
}
y /= std::pow(h, alpha); // divide by the power of h
return y;
}
3.2 基于具体问题的求解
举一个分数阶微分方程的例子:
C D 0.5 y ( t ) + y ( t ) = 1 , y ( 0 ) = 0. {}_{\mathrm{C}}D^{0.5} y(t) + y(t) = 1, \quad y(0) = 0. CD0.5y(t)+y(t)=1,y(0)=0.
这是一个分数阶阻尼振荡器的模型,它描述了一个具有分数阶粘弹性的弹簧的运动。它的解析解是:
y ( t ) = 1 − E 0.5 ( − t 0.5 ) , y(t) = 1 - E_{0.5}(-t^{0.5}), y(t)=1−E0.5(−t0.5),
其中 E 0.5 E_{0.5} E0.5 是 M i t t a g − L e f f l e r Mittag-Leffler Mittag−Leffler 函数,它是分数阶微分方程的常见解。这个方程的数值解可以用 MATLAB 的 ode45 函数求得:(要用到前面定义的fderiv函数哦):
% define the function handle for the fractional derivative
f = @(t,y) 1 - y - 0.5 * fderiv(@(s) y, t, 0.5, 0.01);
% solve the fractional differential equation using ode45
[t,y] = ode45(f, [0,10], 0);
% plot the solution
plot(t,y)
xlabel('t')
ylabel('y')
title('Solution of the fractional differential equation')
3.3 几个特殊的阶数
对于特殊阶数的分数阶求导, 1 2 \frac{1}{2} 21 阶导数是再合适不过的例子了,在这里给各位站友们介绍如何求解它们的简便方法。
首先,分数阶导数的定义可以通过多种方式给出,最常见的是 R i e m a n n − L i o u v i l l e Riemann-Liouville Riemann−Liouville分数阶导数和 C a p u t o Caputo Caputo分数阶导数(老生常谈的话题了)。对于函数 f ( t ) f(t) f(t),其 R i e m a n n − L i o u v i l l e Riemann-Liouville Riemann−Liouville分数阶导数定义为:
D a + α f ( t ) = 1 Γ ( n − α ) d n d t n ∫ a t f ( τ ) ( t − τ ) α − n + 1 d τ , n − 1 < α < n D_{a+}^{\alpha}f(t) = \frac{1}{\Gamma(n-\alpha)} \frac{d^n}{dt^n} \int_a^t \frac{f(\tau)}{(t-\tau)^{\alpha-n+1}} d\tau, \quad n-1 < \alpha < n Da+αf(t)=Γ(n−α)1dtndn∫at(t−τ)α−n+1f(τ)dτ,n−1<α<n
其中, Γ \Gamma Γ是伽马函数, n n n 是大于 α \alpha α的最小整数。
对于 1 / 2 1/2 1/2阶导数,我们可以使用上述定义,其中 α = 1 / 2 \alpha = 1/2 α=1/2,并且 n = 1 n = 1 n=1。
于是:
D a + 1 / 2 f ( t ) = 1 Γ ( 1 / 2 ) d d t ∫ a t f ( τ ) π ( t − τ ) d τ D_{a+}^{1/2}f(t) = \frac{1}{\Gamma(1/2)} \frac{d}{dt} \int_a^t \frac{f(\tau)}{\sqrt{\pi(t-\tau)}} d\tau Da+1/2f(t)=Γ(1/2)1dtd∫atπ(t−τ)f(τ)dτ
求解分数阶导数的方法通常涉及到变换方法,如拉普拉斯变换和傅里叶变换,以及特殊函数,如 M i t t a g − L e f f l e r Mittag-Leffler Mittag−Leffler函数。
以下是一些特殊函数的 1 / 2 1/2 1/2阶导数的表格:
函数 | 1 / 2 1/2 1/2阶导数 |
---|---|
e t e^t et | e t π t \frac{e^t}{\sqrt{\pi t}} πtet |
t n t^n tn | t n − 1 2 Γ ( n + 1 2 ) \frac{t^{n-\frac{1}{2}}}{\Gamma(n+\frac{1}{2})} Γ(n+21)tn−21 |
sin ( t ) \sin(t) sin(t) | cos ( t ) 2 π t − sin ( t ) 2 π Γ ( 1 2 , t ) \frac{\cos(t)}{2\sqrt{\pi t}} - \frac{\sin(t)}{2\sqrt{\pi}} \Gamma(\frac{1}{2}, t) 2πtcos(t)−2πsin(t)Γ(21,t) |
cos ( t ) \cos(t) cos(t) | sin ( t ) 2 π t − cos ( t ) 2 π Γ ( 1 2 , t ) \frac{\sin(t)}{2\sqrt{\pi t}} - \frac{\cos(t)}{2\sqrt{\pi}} \Gamma(\frac{1}{2}, t) 2πtsin(t)−2πcos(t)Γ(21,t) |
同样,对于 1 3 \frac{1}{3} 31阶导数,直接把值代入就可以了。
D
a
+
1
/
3
f
(
t
)
=
1
Γ
(
2
/
3
)
d
d
t
∫
a
t
f
(
τ
)
(
t
−
τ
)
2
/
3
d
τ
D_{a+}^{1/3}f(t) = \frac{1}{\Gamma(2/3)} \frac{d}{dt} \int_a^t \frac{f(\tau)}{(t-\tau)^{2/3}} d\tau
Da+1/3f(t)=Γ(2/3)1dtd∫at(t−τ)2/3f(τ)dτ
给出一些特殊函数的
1
/
3
1/3
1/3阶导数的表格:
函数 | 1 / 3 1/3 1/3阶导数 |
---|---|
e t e^t et | 3 e t 2 Γ ( 2 3 ) t 1 / 3 \frac{3e^t}{2\Gamma(\frac{2}{3})t^{1/3}} 2Γ(32)t1/33et |
t n t^n tn | t n − 1 3 Γ ( n + 2 3 ) \frac{t^{n-\frac{1}{3}}}{\Gamma(n+\frac{2}{3})} Γ(n+32)tn−31 |
sin ( t ) \sin(t) sin(t) | 3 cos ( t ) 2 Γ ( 2 3 ) t 1 / 3 − sin ( t ) 2 Γ ( 2 3 ) Γ ( 1 3 , t ) \frac{3\cos(t)}{2\Gamma(\frac{2}{3})t^{1/3}} - \frac{\sin(t)}{2\Gamma(\frac{2}{3})} \Gamma(\frac{1}{3}, t) 2Γ(32)t1/33cos(t)−2Γ(32)sin(t)Γ(31,t) |
cos ( t ) \cos(t) cos(t) | 3 sin ( t ) 2 Γ ( 2 3 ) t 1 / 3 − cos ( t ) 2 Γ ( 2 3 ) Γ ( 1 3 , t ) \frac{3\sin(t)}{2\Gamma(\frac{2}{3})t^{1/3}} - \frac{\cos(t)}{2\Gamma(\frac{2}{3})} \Gamma(\frac{1}{3}, t) 2Γ(32)t1/33sin(t)−2Γ(32)cos(t)Γ(31,t) |
4. 分数阶导的应用
4.1 初析
分数阶导数的求解应用涉及到分数阶微分方程,即包含分数阶导数的方程。分数阶微分方程可以用来建模许多自然科学和工程领域的问题,例如流体力学,电路理论,粘弹性,生物物理,信号处理,图像处理,金融数学等。
以下是一些具体的例子:
- 在电路和网络中,分数阶导数可以用来模拟一些具有分数阶阻抗或电容的元件,例如分数阶电感器、分数阶电容器、分数阶电阻器等6。这些元件可以提高电路的灵敏度、带宽、稳定性等性能。例如,以下是一个含有分数阶电感器的电路的微分方程:
L α d α i d t α + R i = V L^{\alpha}\frac{d^{\alpha}i}{dt^{\alpha}}+Ri=V Lαdtαdαi+Ri=V
- 其中 L α L^{\alpha} Lα 是分数阶电感器的分数阶电感, R R R 是电阻, V V V 是电压, i i i 是电流, α \alpha α 是分数阶参数。这个方程可以用数值方法求解,例如 G r u n w a l d − L e t n i k o v Grunwald-Letnikov Grunwald−Letnikov 公式7。
- 在非局部效应中,分数阶导数可以用来描述一些具有长程相互作用或历史依赖的系统,例如粘弹性材料、变粘性流体、热传导、波动等8。这些系统的动力学行为不能用整数阶微分方程来刻画,而需要用分数阶微分方程来描述。例如,以下是一个描述粘弹性材料的分数阶 K e l v i n − V o i g t Kelvin-Voigt Kelvin−Voigt 模型的微分方程:
E 0 d α u d t α + E 1 u = F E_0\frac{d^{\alpha}u}{dt^{\alpha}}+E_1u=F E0dtαdαu+E1u=F
- 其中 E 0 E_0 E0 和 E 1 E_1 E1 是材料的弹性模量, u u u 是位移, F F F 是外力, α \alpha α 是分数阶参数。这个方程可以用数值方法求解,例如 A d a m s − B a s h f o r t h − M o u l t o n Adams-Bashforth-Moulton Adams−Bashforth−Moulton 方法9。
- 在概率和统计中,分数阶导数可以用来建立一些具有非高斯分布或非马尔可夫性的随机过程,例如分数阶布朗运动、分数阶扩散过程、分数阶随机微分方程等10。这些过程可以用来模拟一些具有异常扩散或长记忆的现象,例如生物细胞的运动、金融市场的波动、地震的发生等11。例如,以下是一个描述分数阶布朗运动的随机微分方程:
d X t = μ d t + σ d W t α dX_t = \mu dt + \sigma dW_t^{\alpha} dXt=μdt+σdWtα
- 其中 X t X_t Xt 是分数阶布朗运动的位置, μ \mu μ 是漂移系数, σ \sigma σ 是扩散系数, W t α W_t^{\alpha} Wtα 是分数阶维纳过程, α \alpha α 是分数阶参数。这个方程可以用数值方法求解,例如 E u l e r − M a r u y a m a Euler-Maruyama Euler−Maruyama 方法12。
- 在分形和随机介质中,分数阶导数可以用来刻画一些具有自相似性或多尺度性的结构,例如分形维数、分形维度谱、分形布朗运动、分形随机场等 。这些结构可以用来描述一些具有复杂几何形状或不均匀性质的物理系统,例如多孔介质、裂缝岩石、分形图像、分形信号等 。例如,以下是一个描述分形维数的公式:
D f = lim ϵ → 0 log N ( ϵ ) log ( 1 / ϵ ) D_f = \lim_{\epsilon \to 0} \frac{\log N(\epsilon)}{\log (1/\epsilon)} Df=ϵ→0limlog(1/ϵ)logN(ϵ)
- 其中 D f D_f Df 是分形维数, N ( ϵ ) N(\epsilon) N(ϵ) 是用边长为 ϵ \epsilon ϵ 的小方块覆盖分形所需的最小数量。这个公式可以用数值方法求解,例如盒计数法 。
4.2 方法介绍
所以,光看别人是怎么在实际工程问题应用的,还是感觉有点拿捏不住。那这里就具体地来介绍一种方法吧。
分数阶微分方程的求解方法其实有 1 m o l 1 mol 1mol 多,其中一种常用的方法是基于 G r u ¨ n w a l d − L e t n i k o v Grünwald-Letnikov Gru¨nwald−Letnikov 公式的数值方法。 G r u ¨ n w a l d − L e t n i k o v Grünwald-Letnikov Gru¨nwald−Letnikov 公式是一种用差分近似分数阶导数的公式:
a D t α f ( t ) ≈ 1 h α ∑ j = 0 k ( − 1 ) j ( α j ) f ( t − j h ) , h = t − a k {}_{a}D^{\alpha}_t f(t) \approx \frac{1}{h^{\alpha}} \sum_{j=0}^k (-1)^j \binom{\alpha}{j} f(t-jh), \quad h = \frac{t-a}{k} aDtαf(t)≈hα1j=0∑k(−1)j(jα)f(t−jh),h=kt−a
p . s . p.s. p.s. 其实也就是上面说过的 G r u n w a l d − L e t n i k o v Grunwald-Letnikov Grunwald−Letnikov 公式,就不再赘述啦~
关键是下面的——
基于 G r u ¨ n w a l d − L e t n i k o v Grünwald-Letnikov Gru¨nwald−Letnikov 公式的数值方法可以将分数阶微分方程转化为一阶常微分方程组,然后用常用的数值方法如 E u l e r Euler Euler 法, R u n g e − K u t t a Runge-Kutta Runge−Kutta 法等求解。具体的算法步骤如下:
- 给定分数阶微分方程 a D t α y ( t ) = f ( t , y ( t ) ) {}_{a}D^{\alpha}_t y(t) = f(t,y(t)) aDtαy(t)=f(t,y(t)),初始条件 y ( a ) = y 0 y(a) = y_0 y(a)=y0,分数阶导数的阶数 α ∈ ( 0 , 1 ) \alpha \in (0,1) α∈(0,1),求解区间 [ a , b ] [a,b] [a,b],步长 h h h,步数 k k k。
- 用 G r u ¨ n w a l d − L e t n i k o v Grünwald-Letnikov Gru¨nwald−Letnikov 公式将分数阶微分方程转化为一阶常微分方程组13:
{ y 0 ′ ( t ) = 1 h α [ f ( t , y 0 ( t ) ) − ( α 1 ) y 0 ( t ) ] y j ′ ( t ) = 1 h α [ y j − 1 ( t ) − ( α j + 1 ) y j ( t ) ] , j = 1 , 2 , … , k − 1 y k ′ ( t ) = 1 h α y k − 1 ( t ) \begin{cases} y_0'(t) = \frac{1}{h^{\alpha}} \left[ f(t,y_0(t)) - \binom{\alpha}{1} y_0(t) \right] \\ y_j'(t) = \frac{1}{h^{\alpha}} \left[ y_{j-1}(t) - \binom{\alpha}{j+1} y_j(t) \right], \quad j = 1,2,\dots,k-1 \\ y_k'(t) = \frac{1}{h^{\alpha}} y_{k-1}(t) \end{cases} ⎩ ⎨ ⎧y0′(t)=hα1[f(t,y0(t))−(1α)y0(t)]yj′(t)=hα1[yj−1(t)−(j+1α)yj(t)],j=1,2,…,k−1yk′(t)=hα1yk−1(t)
- 其中 y 0 ( t ) = y ( t ) y_0(t) = y(t) y0(t)=y(t) 是原方程的解, y j ( t ) y_j(t) yj(t) 是辅助变量,初始条件为 y j ( a ) = 0 y_j(a) = 0 yj(a)=0, j = 1 , 2 , … , k j = 1,2,\dots,k j=1,2,…,k。
- 用数值方法如 E u l e r Euler Euler 法, R u n g e − K u t t a Runge-Kutta Runge−Kutta 法等求解一阶常微分方程组,得到 y 0 ( t ) y_0(t) y0(t) 的近似解,即 y ( t ) y(t) y(t) 的近似解。
4.3 一起来实战吧
以一个例题为例,假设要求解如下的分数阶微分方程14:
0 D t 0.5 y ( t ) = − y ( t ) + t , t ∈ [ 0 , 10 ] , y ( 0 ) = 1 {}_{0}D^{0.5}_t y(t) = -y(t) + t, \quad t \in [0,10], \quad y(0) = 1 0Dt0.5y(t)=−y(t)+t,t∈[0,10],y(0)=1
其中 α = 0.5 \alpha = 0.5 α=0.5, f ( t , y ( t ) ) = − y ( t ) + t f(t,y(t)) = -y(t) + t f(t,y(t))=−y(t)+t, y 0 = 1 y_0 = 1 y0=1, a = 0 a = 0 a=0, b = 10 b = 10 b=10。我们取步长 h = 0.01 h = 0.01 h=0.01,步数 k = 100 k = 100 k=100,用四阶 Runge-Kutta 法求解一阶常微分方程组。
数理过程我就不写啦,就说说具体是怎么代码实现的吧~
MATLAB 代码如下:
% Define the parameters
alpha = 0.5; % fractional order
f = @(t,y) -y + t; % right hand side function
y0 = 1; % initial condition
a = 0; % left endpoint
b = 10; % right endpoint
h = 0.01; % step size
k = 100; % step number
% Define the binomial coefficients
binom = zeros(1,k+1);
binom(1) = 1;
for j = 1:k
binom(j+1) = binom(j) * (alpha - j + 1) / j;
end
% Define the Runge-Kutta function
function [t,y] = rk4(f,a,b,h,y0)
% Input: f is the function handle
% a and b are the left and right endpoints
% h is the step size
% y0 is the initial condition
% Output: t is the vector of time points
% y is the vector of solution values
t = a:h:b; % create the time vector
n = length(t); % get the number of steps
y = zeros(1,n); % initialize the solution vector
y(1) = y0; % set the initial condition
for i = 1:n-1 % loop over the steps
% calculate the slopes
k1 = f(t(i),y(i));
k2 = f(t(i)+h/2,y(i)+h*k1/2);
k3 = f(t(i)+h/2,y(i)+h*k2/2);
k4 = f(t(i)+h,y(i)+h*k3);
% update the solution
y(i+1) = y(i) + h/6 * (k1 + 2*k2 + 2*k3 + k4);
end
end
% Define the auxiliary variables
y = zeros(k+1,1); % initialize the solution vector
y(1) = y0; % set the initial condition
z = zeros(k,1); % initialize the auxiliary vector
z(1) = y0; % set the initial condition
% Solve the fractional differential equation
for i = 1:k % loop over the steps
% solve the first equation using Runge-Kutta method
[t,y] = rk4(@(t,y) 1/h^alpha * (f(t,y) - binom(2) * y),a,a+h,y(1));
% update the solution vector
y(i+1) = y(end);
% solve the other equations using Runge-Kutta method
for j = 2:k
[t,z] = rk4(@(t,z) 1/h^alpha * (y(j-1) - binom(j+1) * z(j)),a,a+h,z(j));
% update the auxiliary vector
z(j) = z(end);
end
end
% Plot the solution
plot(0:h:b,y,'b')
xlabel('t')
ylabel('y(t)')
title('Solution of the fractional differential equation')
grid on
然后是C++ 代码:
// Include the necessary libraries
#include <iostream>
#include <cmath>
#include <vector>
#include <limits>
using namespace std;
// Define the parameters
const double alpha = 0.5; // fractional order
const double y0 = 1.0; // initial condition
const double a = 0.0; // left endpoint
const double b = 10.0; // right endpoint
const double h = 0.01; // step size
const int k = 100; // step number
// Define the right hand side function
double f(double t, double y) {
return -y + t;
}
// Define the Runge-Kutta function
void rk4(double (*f)(double, double), double a, double b, double h, double y0, vector<double>& t, vector<double>& y) {
// Input: f is the function pointer
// a and b are the left and right endpoints
// h is the step size
// y0 is the initial condition
// Output: t is the vector of time points
// y is the vector of solution values
int n = (b - a) / h; // get the number of steps
t.resize(n+1); // resize the time vector
y.resize(n+1); // resize the solution vector
t[0] = a; // set the initial time
y[0] = y0; // set the initial condition
for (int i = 0; i < n; i++) { // loop over the steps
// calculate the slopes
double k1 = f(t[i], y[i]);
double k2 = f(t[i] + h/2, y[i] + h*k1/2);
double k3 = f(t[i] + h/2, y[i] + h*k2/2);
double k4 = f(t[i] + h, y[i] + h*k3);
// update the solution
y[i+1] = y[i] + h/6 * (k1 + 2*k2 + 2*k3 + k4);
// update the time
t[i+1] = t[i] + h;
}
}
// Define the main function
int main() {
// Define the binomial coefficients
vector<double> binom(k+1);
binom[0] = 1;
for (int j = 1; j <= k; j++) {
binom[j] = binom[j-1] * (alpha - j + 1) / j;
}
// Define the auxiliary variables
vector<double> y(k+1); // initialize the solution vector
y[0] = y0; // set the initial condition
vector<double> z(k); // initialize the auxiliary vector
z[0] = y0; // set the initial condition
// Solve the fractional differential equation
for (int i = 0; i < k; i++) { // loop over the steps
// solve the first equation using Runge-Kutta method
vector<double> t, y_temp;
rk4([](double t, double y) {return 1/pow(h,alpha) * (f(t,y) - binom[1] * y);}, a, a+h, h, y[i], t, y_temp);
// update the solution vector
y[i+1] = y_temp.back();
// solve the other equations using Runge-Kutta method
for (int j = 1; j < k; j++) {
vector<double> t, z_temp;
rk4([=](double t, double z) {return 1/pow(h,alpha) * (y[j-1] - binom[j+1] * z);}, a, a+h, h, z[j], t, z_temp);
// update the auxiliary vector
z[j] = z_temp.back();
}
}
// Print the solution
for (int i = 0; i <= k; i++) {
cout << "y(" << i*h << ") = " << y[i] << endl;
}
return 0;
}
尾声
本文讲了好多好多干货,可能一时间消化不了。建议抽空的时候反复看,多消化。
当你看懂之后,也许,会浮现出一年前,在那个汗水被冷风凝固的冬夜,你聚精会神地听着张宇的闭关修炼时,期盼现在的你一个月后折桂的一路生花吧~
祝君成功上岸,功不唐捐!
参考
^Fractional calculus.” Wikipedia, 6 Jan. 2024. ↩︎
^Weisstein, Eric W. “Fractional Derivative.” MathWorld, Wolfram Research. ↩︎
^Flammable Maths. “The Fractional Derivative, what is it? | Introduction to Fractional Calculus.” YouTube, 20 Jan. 2018. ↩︎
^Almeida, Ricardo, et al. “Fractional derivatives and the fundamental theorem of fractional calculus.” Fractional Calculus and Applied Analysis, vol. 23, no. 6, 2020, pp. 1634-1652. ↩︎
^Li, Changpin, et al. “FdeSolver: A Julia Package for Solving Fractional Differential Equations.” arXiv, Cornell University, 28 Dec. 2021. ↩︎
^user1709. “Applications of Fractional Calculus.” Mathematics Stack Exchange, 30 Sept. 2010. ↩︎
^Almeida, Ricardo, et al. “A Generalized Definition of the Fractional Derivative with Applications.” Mathematical Problems in Engineering, vol. 2021, 2021, ↩︎
^The Organic Chemistry Tutor. “Limit Definition of Derivative Square Root, Fractions, 1/sqrt(x), Examples - Calculus.” YouTube, 17 May 2017. ↩︎
^Erhan, Ozan. “Fractional Derivatives. And How to Calculate Them.” Medium, 10 Feb. 2019. ↩︎
^Tenreiro Machado, J. A., et al. Fractional Derivative Modeling in Mechanics and Engineering. Springer, 2021. ↩︎
^Sen, Mihir. “Introduction to Fractional-Order Operators and Their Engineering Applications.” University of Notre Dame. ↩︎
^Podlubny, Igor, et al. “Selected Engineering Applications of Fractional-Order Calculus.” In Fractional Calculus: Theory, Applications, and Numerics, edited by Igor Podlubny and J. A. Tenreiro Machado, 323-358. Springer, 2021. ↩︎
^Li, Changpin, et al. “FdeSolver: A Julia Package for Solving Fractional Differential Equations.” arXiv, Cornell University, 28 Dec. 2021. ↩︎
^user1559695. “Implementing the derivative in C/C++.” Stack Overflow, 11 Oct. 2009, ↩︎