為了深入理解fMRI分析的原理,還是要學(xué)習(xí)一些微分方程與動力系統(tǒng)的東東,這是我的學(xué)習(xí)筆記,內(nèi)容還沒有很好的整理。
視頻在這里1-Differential Equations and Dynamical Systems Overview_嗶哩嗶哩_bilibili
課程的原始地址在這里:ME 564 - Mechanical Engineering Analysis (washington.edu)
1. Overview
線性代數(shù)是微分方程的一部分,如果可以的話,大學(xué)課程應(yīng)該將這兩部分一起傳授。這門課程除了基本的模型介紹以外,還會結(jié)合Matlab/Python的代碼來進行具體的實現(xiàn)。
這門課程會介紹以下幾個主題:
- 微積分:簡單回顧微積分的計算,重點是泰勒展開
- 簡單常微分方程(Simple Ordinary Differential Equations, ODEs):即
,可以用于描述很多系統(tǒng)(如最常見的兔子吃草問題)
- 常微分方程組(System of ODEs):即
,常見的問題如在兔子吃草過程中引入其他動物(比如狼)
- 特征值與特征向量
- 非線性系統(tǒng)與混沌:即
,常見的問題如行星運動
- 數(shù)值與計算:用Python或者Matlab
2. 微積分回顧:導(dǎo)數(shù)
導(dǎo)數(shù)反映的是一個函數(shù)對于獨立變量
的變化率。其定義如下:
鏈式法則(Chain Rule):
例子:和
,則:
3. 向量和矩陣建模
【一個天氣模型】在這個模型中,天氣有三種狀態(tài),分別是多云(cloudy)、下雨(rainy)和晴天(nice),已知:
上面公式表示一個概率,例如第一條表示如果前一天的天氣為多云時,則第二天的天氣為多云的概率是0.5,后面以此類推。此時,我們可以用一個向量來表示當(dāng)前的天氣情況:
上式中表示當(dāng)前的天氣為晴天。則第二天的前期狀態(tài)可以根據(jù)之前的概率得到:
不難看出,為:
此時,可以換一種形式來表示:
clear all, close all, clc
A = [0.5 0.5 0.25;
0.25 0.0 0.25;
0.25 0.5 0.5];
xtoday = [1; 0; 0];
for k=1:50
k
xtomorrow = A * xtoday
xtoday = xtomorrow;
end
運行上述代碼可以看到,這個模型中天氣會收斂到一個穩(wěn)定的狀態(tài),。
有意思的事情是,這個穩(wěn)態(tài)值和矩陣特征向量與特征值的關(guān)系??梢杂?code>eig函數(shù)來得到矩陣的特征值與特征向量:
[T,D]=eig(A)
T =
-0.6667 -0.7071 0.4082
-0.3333 0.0000 -0.8165
-0.6667 0.7071 0.4082
D =
1.0000 0 0
0 0.2500 0
0 0 -0.2500
此時如果對第一個特征值對應(yīng)的特征向量進行歸一化處理,可以得到:
T(:,1)/sum(T(:,1))
ans =
0.4000
0.2000
0.4000
可以看到其最終結(jié)果就是系統(tǒng)的穩(wěn)態(tài)分布,因此可以用這種方法來計算得到穩(wěn)態(tài)分布,而不是使用迭代的方法。
上面過程的Python代碼如下:
import numpy as np
import matplotlib.pyplot as plt
A = np.array([[0.5, 0.5, 0.25],[0.25, 0, 0.25],[0.25, 0.5, 0.5]])
xtoday = np.array([[1], [0], [0]])
the_weather = np.zeros((3,50))
for k in range(50):
the_weather[:,k] = xtoday[:,0]
xtomorrow = A@xtoday
xtoday = xtomorrow
print(k)
print(xtomorrow)
plt.plot(the_weather.transpose())
plt.grid(True)

4. 簡單常微分方程和它的指數(shù)解
簡單常微分方程的形式:
【例子】假設(shè)在一個草原上有只兔子,兔子的增長速度與兔子的數(shù)量成正比。這個問題就可以描述為上面的公式:
其中,表示增長率,
是時間的函數(shù)。后面我們還會延續(xù)這個模型,引入一些其他的動物。
4.1 微分方程求解
要想計算這個微分方程,有很多種方法:
【method 1】
根據(jù)上式,不難得到以下結(jié)論:
易知上述微分方程的差分形式為:
5. 使用冪級數(shù)求解微分方程
對于常微分方程,假設(shè)函數(shù)
可以寫成冪級數(shù)的形式,可知:
則
同時:
的對應(yīng)系數(shù)應(yīng)該相等,因此:
所以:
6. 泰勒級數(shù)和冪級數(shù)
一個函數(shù)可以在一個點
(
在
處光滑)被泰勒展開:
如果在特定的點(即
)展開,上述公式也可以寫為如下形式:
【例1】當(dāng)時,其泰勒展開為:
【例2】當(dāng)時,其泰勒展開為:
Matlab代碼如下:
clear all, close all
x = -10:0.01:10;
y = sin(x);
plot(x, y, 'k', 'LineWidth',2)
axis([-10 10 -10 10])
grid on, hold on
%% 一階泰勒展開
P = [1 0];
yT1 = polyval(P, x);
plot(x, yT1, 'b--', 'LineWidth', 1.2)
%% 三階泰勒展開
P = [-1/factorial(3) 0 1 0];
yT3 = polyval(P, x);
plot(x, yT3, 'r--', 'LineWidth', 1.2)
%% 五階泰勒展開
P = [1/factorial(5) 0 -1/factorial(3) 0 1 0];
yT5 = polyval(P, x);
plot(x, yT5, 'g--', 'LineWidth', 1.2)
%% 七階泰勒展開
P = [-1/factorial(7) 0 1/factorial(5) 0 -1/factorial(3) 0 1 0];
yT7 = polyval(P, x);
plot(x, yT7, 'm--', 'LineWidth', 1.2)
%% 九階泰勒展開
P = [1/factorial(9) 0 -1/factorial(7) 0 1/factorial(5) 0 -1/factorial(3) 0 1 0];
yT9 = polyval(P, x);
plot(x, yT9, 'c--', 'LineWidth', 1.2)

Python的泰勒展開結(jié)果如下:
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import solve_ivp
from scipy.special import factorial
x = np.linspace(-10, 10, 2000)
y = np.sin(x)
plt.plot(x, y, 'k', linewidth=2)
plt.xlim(-10, 10)
plt.ylim(-10, 10)
plt.grid(True)
# 一階泰勒展開
P = [1, 0]
yT1 = np.polyval(P, x)
plt.plot(x, yT1, 'b--', linewidth=1.2)
# 三階泰勒展開
P = [-1/factorial(3), 0, 1, 0]
yT3 = np.polyval(P, x)
plt.plot(x, yT3, 'r--', linewidth=1.2)
# 五階泰勒展開
P = [1/factorial(5), 0, -1/factorial(3), 0, 1, 0]
yT5 = np.polyval(P, x)
plt.plot(x, yT5, 'g--', linewidth=1.2)
# 七階泰勒展開
P = [-1/factorial(7), 0, 1/factorial(5), 0, -1/factorial(3), 0, 1, 0]
yT7 = np.polyval(P, x)
plt.plot(x, yT7, 'm--', linewidth=1.2)
# 九階泰勒展開
P = [1/factorial(9), 0, -1/factorial(7), 0, 1/factorial(5), 0, -1/factorial(3), 0, 1, 0]
yT9 = np.polyval(P, x)
plt.plot(x, yT9, 'c--', linewidth=1.2)

7.指數(shù)函數(shù)的泰勒級數(shù)和歐拉公式
本節(jié)課利用泰勒級數(shù)推導(dǎo)出歐拉公式,過程如下:
得到上述公式后,后面的計算會變得更加容易
8. 用二階常微分方程描述諧振子
此處,我們引入了一個物理學(xué)中的常見概念——諧振子,并使用微分方程來對其狀態(tài)進行建模。
問題描述如下:在一個光滑平面上,有一個質(zhì)量為 的物體,其一端用彈簧與墻壁相連,彈簧的系數(shù)為k,物體偏移靜止?fàn)顟B(tài)(即除支撐力外,不受任何彈簧彈力)的位移為
,則可知:
令,
,則:
求解上述方程的方法有很多種:
【Method 1】瞪眼法
因為,
,結(jié)合前面的泰勒展開結(jié)果,充分考慮這些初始條件,則可以猜測
則
而上述結(jié)果滿足之前的,于是瞪眼成功。
對于一般的和
,
【Method 2】泰勒級數(shù)展開
因此
將其帶回,可得:
【Method 3】瞪眼法2
一個函數(shù)求導(dǎo)之后仍然是自身的函數(shù),最容易想到的就是指數(shù)函數(shù),因此不妨猜測是一種指數(shù)函數(shù)。
將其代入微分方程,可得:
所以:
【Method 4】Suspend variable
在該方法中,引入一個新的變量
令:
則:
9. 二階ODE,阻尼震蕩器

新的問題變成了添加了一個阻尼的震蕩子,如上圖所示,此時模型變?yōu)椋?br>
令
所以:
看到這個模型,不難繼續(xù)要使用瞪眼法,令:
所以:
這個方程被稱為特征方程。
其解為:
最終
還可以將模型寫為矩陣形式:
下面我們在Matlab中對這個過程進行模擬,需要注意的是:
看懂了上式就可以對上述過程進行編碼了,具體代碼如下
clear all; close all;
w = 2*pi;
d = 0.25;
% dt = Ax,此處和文中給出的公式并不一致,相當(dāng)于k=.5,但是只要知道這是在做一些系統(tǒng)初始化的設(shè)置即可
A = [0 1; -w^2 -2*d*w];
dt = .01; % time step
T = 10; % amount of time to integrate
% 初始位置在2處,初始速度為0
x0 = [2; 0];
xF(:,1) = x0;
tF(1) = 0;
for k=1:T/dt
tF(k+1) = k*dt;
xF(:,k+1) = (eye(2) + dt*A)*xF(:,k);
end
plot(tF, xF(1,:), 'k')
% 使用龍格庫塔(Runge Kutta)得到的結(jié)果
[t, xGood] = ode45( @(t, x) A*x, 0:dt:T, x0);
hold on
plot(t, xGood(:,1), 'r')
xlabel('Time (s)')
ylabel('Position (m)')
legend('Forward Euler', 'ODE45(RK4)')
figure
plot(xGood(:,1),xGood(:,2),'k')
xlabel('Position [m]')
ylabel('Velocity [m/s]')


import numpy as np
from matplotlib import pyplot as plt
from scipy.integrate import solve_ivp
# play around with different 'd' and 'dt' to see various behavior!
w = 2 * np.pi # natural frequency
d = .25 # damping ratio
# spring-mass-damper system
A = np.array([[0, 1], [-w ** 2, -2 * d * w]]) # \dot{x} = Ax
dt = 0.01 # time step
T = 10 # amount of time to integrate
n = int(T / dt)
t = np.linspace(0, T, n)
x0 = [2, 0] # initial condition (x=2, v=0)
# iterate forward Euler
xF = np.zeros((2, n))
xF[:, 0] = x0
for k in range(n - 1):
xF[:, k + 1] = (np.eye(2) + dt * A) @ xF[:, k]
# compute better integral using built-in python code
# 4th-order Runge Kutta
def linear_ode(t, x):
return A @ x # @ symbol for matrix-vector product here
linear_ode_solution = solve_ivp(linear_ode, (0, T), x0, t_eval=t)
xGood = linear_ode_solution.y
plt.figure(figsize=(20, 4))
plt.subplot(1, 2, 1)
plt.plot(t, xF[0, :], 'k')
plt.plot(t, xGood[0, :],'r')
plt.xlabel('Time [s]')
plt.ylabel('Position [m]')
plt.legend(['Forward Euler', 'ODE45 (RK4)'])
plt.grid(True)
plt.subplot(1, 2, 2)
plt.plot(xF[0, :], xF[1, :], 'k')
plt.plot(xGood[0, :], xGood[1, :], 'r')
plt.xlabel('Position [m]')
plt.ylabel('Velocity [m/s]')
plt.legend(['Forward Euler', 'ODE45 (RK4)'])
plt.grid(True)
