DW学习笔记|数学建模task2
本章主要涉及到的知识点有:
- 微分方程的解法
- 如何用 Python 解微分方程
- 偏微分方程及其求解方法
- 微分方程的基本案例
- 差分方程的求解
- 数值计算方法
- 元胞自动机
2.1 微分方程的理论基础
微分方程是什么?如果你参加过高考,可能在高三备考中遇到过这样的问题:给定函数 f ( x ) f(x) f(x) 及其导数之间的等式,然后分析函数的性质,如单调性、零点等,但没有给出函数的解析式。这时你可能会想,如果能通过这个方程求出函数的通项形式该多好!微分方程的目的就是这样,它通过将函数 f f f 和它的若干阶导数联系起来形成一个方程(组),来求出函数的解析式或函数值随自变量变化的曲线。
2.1.1 函数、导数与微分
微分和导数其实是紧密相关的概念。我们通常将导数理解为函数在某一点处切线的斜率。而微分则描述的是当我们对自变量 d x dx dx 施加一个非常小的增量 d x dx dx 时,函数值相应的变化量与 d x dx dx 之间的关系。当 d x dx dx 非常小的时候,函数的变化量就接近于在该点处切线的变化量 d y dy dy。因此,我们可以用这种方式来理解微分:
d y d x = f ′ ( x ) . \frac{dy}{dx} = f'(x). dxdy=f′(x).
微分实际上描述的是点 M M M 处切线的斜率;导数则描述的是割线 M N MN MN 的斜率。但当 d x dx dx 足够小的时候,切线的斜率和割线的斜率就会非常接近,这就是微分的核心概念。而微分方程,就是描述函数与其导数之间关系的方程。
2.1.2 一阶线性微分方程的解
一阶线性微分方程描述的是怎么一回事呢?它是指形如下方的方程:
d y d x + P ( x ) y = Q ( x ) . \frac{dy}{dx} + P(x)y = Q(x). dxdy+P(x)y=Q(x).
这里的 y y y 是一个未知函数,而 P P P 和 Q Q Q 是已知的函数。我们的目标是找出 y y y 的解,即它的通解形式。为了解这个方程,我们通常会使用分离变量积分法和常数变易法这两种方法。首先,我们尝试解一个特殊情况的齐次方程,即当 Q ( x ) = 0 Q(x) = 0 Q(x)=0 时:
d y d x + P ( x ) y = 0. \frac{dy}{dx} + P(x)y = 0. dxdy+P(x)y=0.
通过变量分离和变形,我们可以得到:
1 y d y = − P ( x ) d x . \frac{1}{y} dy = -P(x) dx. y1dy=−P(x)dx.
接着,对两边进行不定积分,我们可以得到解的通式为
y = C exp { − ∫ P ( x ) d x } , y = C \exp\left\{-\int P(x) \, dx\right\}, y=Cexp{−∫P(x)dx},
其中 C C C 是一个常数。但在一般情况下, Q ( x ) Q(x) Q(x) 不一定为 0,所以我们需要将常数 C C C 替换为一个函数 C ( x ) C(x) C(x),然后对 y y y 求导并将其代入原方程中以求得 C ( x ) C(x) C(x) 的通解。这就是所谓的常数变易法。有兴趣的读者可以进一步推导出方程的通解为(其中 C C C 为常数):
y = exp { − ∫ P ( x ) d x } [ ∫ Q ( x ) exp { ∫ P ( x ) d x } d x + C ] . y = \exp\left\{-\int P(x) \, dx\right\} \left[\int Q(x) \exp\left\{\int P(x) \, dx\right\} \, dx + C \right]. y=exp{−∫P(x)dx}[∫Q(x)exp{∫P(x)dx}dx+C].
2.1.3 二阶常系数线性微分方程的解
二阶常系数线性微分方程可以表示为:
f ′ ′ ( x ) + p f ′ ( x ) + q f ( x ) = C ( x ) . f''(x) + p f'(x) + q f(x) = C(x). f′′(x)+pf′(x)+qf(x)=C(x).
这个方程关联了二阶导数、一阶导数和函数本身。解决这个方程的一般策略是先考虑对应的齐次方程,即让 C ( x ) C(x) C(x) 为 0:
f ′ ′ ( x ) + p f ′ ( x ) + q f ( x ) = 0. f''(x) + p f'(x) + q f(x) = 0. f′′(x)+pf′(x)+qf(x)=0.
解这种二阶常系数齐次线性微分方程时,我们通常使用特征根法。这个方法的关键是求解特征方程:
r 2 + p r + q = 0. r^2 + pr + q = 0. r2+pr+q=0.
这个齐次方程的解的形式取决于特征方程的根。根据特征方程的不同实根、相同实根、或共轭复根,齐次微分方程的解会有不同的形式:
{ y = C 1 e α 1 x + C 2 e α 2 x , r 1 = α 1 , r 2 = α 2 y = ( C 1 x + C 2 ) e α x , r 1 = r 2 = α y = e α x [ C 1 sin ( β x ) + C 2 cos ( β x ) ] , r = α ± β i \begin{cases} y = C_1 e^{\alpha_1 x} + C_2 e^{\alpha_2 x}, & r_1 = \alpha_1, r_2 = \alpha_2 \\ y = (C_1 x + C_2) e^{\alpha x}, & r_1 = r_2 = \alpha \\ y = e^{\alpha x} [C_1 \sin(\beta x) + C_2 \cos(\beta x)], & r = \alpha \pm \beta i \end{cases} ⎩ ⎨ ⎧y=C1eα1x+C2eα2x,y=(C1x+C2)eαx,y=eαx[C1sin(βx)+C2cos(βx)],r1=α1,r2=α2r1=r2=αr=α±βi
对于一般的二阶非齐次线性微分方程,我们可以根据右侧 C ( x ) C(x) C(x) 的形式推导出一个特解。非齐次方程的通解等于齐次方程的通解加上非齐次方程的特解。求微分方程的特解有时需要观察法,但幸运的是,存在两种特殊形式:
C ( x ) = P m ( x ) e λ x , C ( x ) = e λ x [ P m cos ( ω x ) + Q n ( x ) sin ( ω x ) ] . C(x) = P_m(x) e^{\lambda x}, \quad C(x) = e^{\lambda x} [P_m \cos(\omega x) + Q_n(x) \sin(\omega x)]. C(x)=Pm(x)eλx,C(x)=eλx[Pmcos(ωx)+Qn(x)sin(ωx)].
其中, P m ( x ) P_m(x) Pm(x) 是一个 m m m 次多项式, Q n ( x ) Q_n(x) Qn(x) 是一个 n n n 次多项式。这两种形式的特解分别为:
f ( x ) = x k P m ( x ) e λ x , f ( x ) = x k e λ x [ P i cos ( ω x ) + Q i ( x ) sin ( ω x ) ] . f(x) = x^k P_m(x) e^{\lambda x}, \quad f(x) = x^k e^{\lambda x} [P_i \cos(\omega x) + Q_i(x) \sin(\omega x)]. f(x)=xkPm(x)eλx,f(x)=xkeλx[Picos(ωx)+Qi(x)sin(ωx)].
其中 k k k 的取值取决于特征方程根的个数:如果有两个不同的实根,则 k = 2 k=2 k=2;如果有两个相同的实根,则 k = 1 k=1 k=1;如果没有实根,则 k = 0 k=0 k=0。通过上述形式,我们可以解出二阶线性微分方程。
2.1.4 利用 Python 求函数的微分与积分
在 Python 中,我们可以使用 Numpy 和 SciPy 这两个库来进行函数的微分和积分计算。下面将通过具体示例来说明如何使用这些库来求解函数的微分和积分。 假设我们需要计算函数 f ( x ) = cos ( 2 π x ) exp ( − x ) + 1.2 f(x) = \cos(2\pi x) \exp(-x) + 1.2 f(x)=cos(2πx)exp(−x)+1.2 在区间 [ 0 , 0.7 ] [0, 0.7] [0,0.7] 上的定积分。我们可以使用 SciPy 库中的 quad
函数来完成这个任务:
import numpy as np
from scipy.integrate import quad# 定义函数
def f(x):return np.cos(2 * np.pi * x) * np.exp(-x) + 1.2# 计算定积分
integral, error = quad(f, 0, 0.7)
print(f'定积分的结果是:{integral}')
输出结果:
定积分的结果是:0.7951866427656943
除了使用 SciPy 库中的 quad
函数求解定积分外,我们还可以使用数值积分的方法来近似计算。一种常见的数值积分方法是梯形法则。下面我们将通过一个示例来说明如何使用梯形法则来近似计算函数的定积分。 假设我们需要计算函数 f ( x ) = cos ( 2 π x ) exp ( − x ) + 1.2 f(x) = \cos(2\pi x) \exp(-x) + 1.2 f(x)=cos(2πx)exp(−x)+1.2 在区间 [ 0 , 0.7 ] [0, 0.7] [0,0.7] 上的定积分。我们可以使用 Numpy 库中的 trapz
函数来完成这个任务:
import numpy as np# 定义函数
def f(x):return np.cos(2 * np.pi * x) * np.exp(-x) + 1.2# 定义积分区间
a, b = 0, 0.7# 定义积分步长
n = 1000
x = np.linspace(a, b, n)
y = f(x)# 计算定积分
integral = np.trapz(y, x)
print(f'定积分的结果是:{integral}')
输出结果:
定积分的结果是:0.7951866427656956
由此可见,使用数值积分的方法可以获得非常接近的结果。通过这些方法,我们可以方便地在 Python 中求解函数的微分和积分问题,为工程和科学计算提供了有力的工具。
- 简易且类似的示例
- 使用Numpy和SciPy库进行函数的微分与积分计算。
- 定积分示例:
from scipy.integrate import quad import numpy as npdef f(x):return np.cos(2 * np.pi * x) * np.exp(-x) + 1.2integral, error = quad(f, 0, 0.7) print(f'定积分的结果是:{integral}')
- 数值微分示例:
import numpy as npx = np.linspace(0, 2, 100) y = x**2 dydx = np.gradient(y, x) derivative_at_1 = dydx[np.argmin(abs(x - 1))] print(f'在x=1处的导数值是:{derivative_at_1}')
2.2 偏微分方程的数值求解
偏微分方程(Partial Differential Equations, PDEs)是针对多元函数的方程,在物理学中有着重要的应用。然而,偏微分方程的求解通常比常微分方程复杂。虽然Python没有专用的PDE求解工具包,但可以通过将连续问题离散化来求解。本节将通过一系列物理案例,展示如何用Python求解典型的偏微分方程。
2.2.1 偏微分方程数值解的理论基础
偏微分方程描述多元函数与其偏导数之间的关系,广泛应用于波动力学、热学、电磁学等领域。常见的二元函数的二阶偏微分方程形式为:
A ∂ 2 f ∂ x 2 + 2 B ∂ 2 f ∂ x ∂ y + C ∂ 2 f ∂ y 2 + D ∂ f ∂ x + E ∂ f ∂ y + F f = 0 (2.3.1) A \frac{\partial^2 f}{\partial x^2} + 2B \frac{\partial^2 f}{\partial x \partial y} + C \frac{\partial^2 f}{\partial y^2} + D \frac{\partial f}{\partial x} + E \frac{\partial f}{\partial y} + F f = 0 \tag{2.3.1} A∂x2∂2f+2B∂x∂y∂2f+C∂y2∂2f+D∂x∂f+E∂y∂f+Ff=0(2.3.1)
其中,根据判别式 (\Delta = B^2 - 4AC) 的值,可以将PDE分为三类:
- 双曲线方程:(\Delta > 0)
- 抛物线方程:(\Delta = 0)
- 椭圆方程:(\Delta < 0)
双曲线方程常用于描述振动与波动,椭圆方程用于稳态问题,如静电场、引力场,抛物线方程用于瞬态问题,如热传导和扩散。
2.2.2 偏微分方程数值解的应用案例
2.2.2.1 RC电路放电过程
使用偏微分方程对RC电路建模,分析电容放电过程中电量随时间的变化。RC电路的方程为:
{ I R + Q C = 0 , I = d Q d t (2.3.2) \begin{cases} IR + \frac{Q}{C} = 0, \\ I = \frac{dQ}{dt} \end{cases} \tag{2.3.2} {IR+CQ=0,I=dtdQ(2.3.2)
通过离散化,得到递推关系并求解:
import numpy as np
import matplotlib.pyplot as pltrc = 2.0
dt = 0.5
n = 1000
t = 0.0
q = 1.0
qt = []
qt0 = []
time = []for i in range(n):t = t + dtq1 = q - q*dt/rcq = q - 0.5*(q1*dt/rc + q*dt/rc)q0 = np.exp(-t/rc)qt.append(q)qt0.append(q0)time.append(t)plt.plot(time, qt, 'o', label='Euler-Modify')
plt.plot(time, qt0, 'r-', label='Analytical')
plt.xlabel('time')
plt.ylabel('charge')
plt.xlim(0, 20)
plt.ylim(-0.2, 1.0)
plt.legend(loc='upper right')
plt.show()
2.2.2.2 一维热传导方程
一维热传导方程是典型的抛物型二阶偏微分方程:
∂ u ∂ t = λ ∂ 2 u ∂ x 2 (2.3.7) \frac{\partial u}{\partial t} = \lambda \frac{\partial^2 u}{\partial x^2} \tag{2.3.7} ∂t∂u=λ∂x2∂2u(2.3.7)
使用有限差分法求解,并绘制温度随时间和空间的变化图:
import numpy as np
import matplotlib.pyplot as plth = 0.1
N = 30
dt = 0.0001
M = 10000
A = dt / (h**2)
U = np.zeros([N+1, M+1])
Space = np.arange(0, (N+1)*h, h)for k in np.arange(0, M+1):U[0, k] = 0.0U[N, k] = 0.0for i in np.arange(0, N):U[i, 0] = 4 * i * h * (3 - i * h)for k in np.arange(0, M):for i in np.arange(1, N):U[i, k+1] = A * U[i+1, k] + (1 - 2 * A) * U[i, k] + A * U[i-1, k]plt.plot(Space, U[:, 0], 'g-', label='t=0')
plt.plot(Space, U[:, 3000], 'b-', label='t=3/10')
plt.plot(Space, U[:, 6000], 'k-', label='t=6/10')
plt.plot(Space, U[:, 9000], 'r-', label='t=9/10')
plt.plot(Space, U[:, 10000], 'y-', label='t=1')
plt.xlabel('x')
plt.ylabel('u(x,t)')
plt.xlim(0, 3)
plt.ylim(-2, 10)
plt.legend(loc='upper right')
plt.show()
2.2.2.3 平流方程
一维平流方程描述某一物理量的平流作用:
∂ u ∂ t + v ∂ u ∂ x = 0 (2.3.11) \frac{\partial u}{\partial t} + v \frac{\partial u}{\partial x} = 0 \tag{2.3.11} ∂t∂u+v∂x∂u=0(2.3.11)
使用一阶迎风格式的差分方法求解,并绘制解的变化图:
import numpy as np
import matplotlib.pyplot as pltdef funcUx_0(x, p):u0 = np.sin(2 * (x - p)**2)return u0v1 = 1.0
p = 0.25
tc = 0
te = 1.0
xa = 0.0
xb = np.pi
dt = 0.02
nNodes = 100nsteps = round(te / dt)
dx = (xb - xa) / nNodes
x = np.arange(xa - dx, xb + 2 * dx, dx)
ux_0 = funcUx_0(x, p)
u = ux_0.copy()
ujp = ux_0.copy()for i in range(nsteps):plt.clf()for j in range(nNodes + 2):ujp[j] = u[j] - (v1 * dt / dx) * (u[j] - u[j - 1])u = ujp.copy()u[0] = u[nNodes + 1]u[nNodes + 2] = u[1]tc += dtplt.plot(x, u, 'b-', label="v1= 1.0")
plt.xlabel("x")
plt.ylabel("U(x)")
plt.legend(loc=(0.05, 0.05))
plt.show()
2.2.2.4 波动方程
二维波动方程的初边值问题描述波的传播:
∂ 2 u ∂ t 2 = c 2 ( ∂ 2 u ∂ x 2 + ∂ 2 u ∂ y 2 ) (2.3.14) \frac{\partial^2 u}{\partial t^2} = c^2 \left( \frac{\partial^2 u}{\partial x^2} + \frac{\partial^2 u}{\partial y^2} \right) \tag{2.3.14} ∂t2∂2u=c2(∂x2∂2u+∂y2∂2u)(2.3.14)
使用有限差分法求解波动方程,并绘制波的传播图:
import numpy as np
import matplotlib.pyplot as pltc = 1.0
tc, te = 0.0, 1.0
xa, xb = 0.0, 1.0
ya, yb = 0.0, 1.0c2 = c * c
dt = 0.01
dx = dy = 0.02
tNodes = round(te / dt)
xNodes = round((xb - xa) / dx)
yNodes = round((yb - ya) / dy)
tZone = np.arange(0, (tNodes + 1) * dt, dt)
xZone = np.arange(xa, xb, dx)
yZone = np.arange(ya, yb, dy)
xx, yy = np.meshgrid(xZone, yZone)
r = 4 * c2 * dt * dt / (dx * dx + dy * dy)
rx = c * c * dt ** 2 / dx ** 2
ry = c * c * dt ** 2 / dy ** 2U = np.zeros([tNodes + 1, xNodes + 1, yNodes + 1])
U[0] = np.sin(6 * np.pi * xx) + np.cos(4 * np.pi * yy)
U[1] = np.sin(6 * np.pi * xx) + np.cos(4 * np.pi * yy)fig = plt.figure(figsize=(8, 6))
ax1 = fig.add_subplot(111, projection='3d')
surf = ax1.plot_surface(xx, yy, U[0, :, :], rstride=2, cstride=2, cmap=plt.cm.coolwarm)for k in range(2, tNodes + 1):for i in range(1, xNodes):for j in range(1, yNodes):U[k, i, j] = rx * (U[k - 1, i - 1, j] + U[k - 1, i + 1, j]) + ry * (U[k - 1, i, j - 1] + U[k - 1, i, j + 1]) + 2 * (1 - rx - ry) * U[k - 1, i, j] - U[k - 2, i, j]surf = ax1.plot_surface(xx, yy, U[k, :, :], rstride=2, cstride=2, cmap='rainbow')
ax1.set_xlim3d(0, 1.0)
ax1.set_ylim3d(0, 1.0)
ax1.set_zlim3d(-2, 2)
ax1.set_title("2D wave equation (t= %.2f)" % (k * dt))
ax1.set_xlabel("x")
ax1.set_ylabel("y")
plt.show()
2.2.2.5 二维热传导方程
二维热传导方程的初边值问题描述热量在平板中的传导:
∂ u ∂ t = λ ( ∂ 2 u ∂ x 2 + ∂ 2 u ∂ y 2 ) + q v (2.3.17) \frac{\partial u}{\partial t} = \lambda \left( \frac{\partial^2 u}{\partial x^2} + \frac{\partial^2 u}{\partial y^2} \right) + q_v \tag{2.3.17} ∂t∂u=λ(∂x2∂2u+∂y2∂2u)+qv(2.3.17)
使用有限差分法求解,并绘制热传导过程中的温度分布图:
import numpy as np
import matplotlib.pyplot as pltdef showcontourf(zMat, xyRange, tNow):x = np.linspace(xyRange[0], xyRange[1], zMat.shape[1])y = np.linspace(xyRange[2], xyRange[3], zMat.shape[0])xx, yy = np.meshgrid(x, y)zMax = np.max(zMat)yMax, xMax = np.where(zMat == zMax)[0][0], np.where(zMat == zMax)[1][0]levels = np.arange(0, 100, 1)showText = "time = {:.1f} s\nhotpoint = {:.1f} C".format(tNow, zMax)plt.plot(x[xMax], y[yMax], 'ro')plt.contourf(xx, yy, zMat, 100, cmap=plt.cm.get_cmap('jet'), origin='lower', levels=levels)plt.annotate(showText, xy=(x[xMax], y[yMax]), xytext=(x[xMax], y[yMax]), fontsize=10)plt.colorbar()plt.xlabel('X')plt.ylabel('Y')plt.title('Temperature distribution of the plate')plt.show()uIni = 25
uBound = 25.0
c = 1.0
qv = 50.0
x_0, y0 = 0.0, 3.0
vx, vy = 2.0, 1.0
tc, te = 0.0, 5.0
xa, xb = 0.0, 16.0
ya, yb = 0.0, 12.0dt = 0.002
dx = dy = 0.1
tNodes = round(te / dt)
xNodes = round((xb - xa) / dx)
yNodes = round((yb - ya) / dy)
xyRange = np.array([xa, xb, ya, yb])
xZone = np.linspace(xa, xb, xNodes + 1)
yZone = np.linspace(ya, yb, yNodes + 1)
xx, yy = np.meshgrid(xZone, yZone)A = (-2) * np.eye(xNodes + 1, k=0) + (1) * np.eye(xNodes + 1, k=-1) + (1) * np.eye(xNodes + 1, k=1)
B = (-2) * np.eye(yNodes + 1, k=0) + (1) * np.eye(yNodes + 1, k=-1) + (1) * np.eye(yNodes + 1, k=1)
rx, ry, ft = c * dt / (dx * dx), c * dt / (dy * dy), qv * dtU = uIni * np.ones((yNodes + 1, xNodes + 1))for k in range(tNodes + 1):t = k * dtxt, yt = x_0 + vx * t, y0 + vy * tQv = qv * np.exp(-((xx - xt)**2 + (yy - yt)**2))U[:, 0] = U[:, -1] = uBoundU[0, :] = U[-1, :] = uBoundU = U + rx * np.dot(U, A) + ry * np.dot(B, U) + Qv * dtif k % 100 == 0:print('t={:.2f}s\tTmax={:.1f} Tmin={:.1f}'.format(t, np.max(U), np.min(U)))showcontourf(U, xyRange, k * dt)
2.2.2.6 二维椭圆方程
二维Poisson方程的差分表达式和数值求解方法:
∂ 2 u ∂ x 2 + ∂ 2 u ∂ y 2 = f ( x , y ) (2.3.21) \frac{\partial^2 u}{\partial x^2} + \frac{\partial^2 u}{\partial y^2} = f(x, y) \tag{2.3.21} ∂x2∂2u+∂y2∂2u=f(x,y)(2.3.21)
采用迭代松弛法递推求得二维椭圆方程的数值解:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3Dxa, xb = 0.0, 1.0
ya, yb = 0.0, 1.0
h = 0.01
w = 0.5
nodes = round((xb - xa) / h)u = np.zeros((nodes + 1, nodes + 1))
for i in range(nodes + 1):u[i, 0] = 1.0 + np.sin(0.5 * (i - 50) / np.pi)u[i, -1] = -1.0 + 0.5 * np.sin((i - 50) / np.pi)u[0, i] = -1.0 + 0.5 * np.sin((i - 50) / np.pi)u[-1, i] = 1.0 + np.sin(0.5 * (50 - i) / np.pi)for iter in range(100):for i in range(1, nodes):for j in range(1, nodes):u[i, j] = w / 4 * (u[i - 1, j] + u[i + 1, j] + u[i, j - 1] + u[i, j + 1]) + (1 - w) * u[i, j]x = np.linspace(0, 1, nodes + 1)
y = np.linspace(0, 1, nodes + 1)
xx, yy = np.meshgrid(x, y)
fig = plt.figure(figsize=(8, 6))
ax = fig.add_subplot(111, projection='3d')
surf = ax.plot_surface(xx, yy, u, cmap=plt.get_cmap('rainbow'))
fig.colorbar(surf, shrink=0.5)
ax.set_xlim3d(0, 1.0)
ax.set_ylim3d(0, 1.0)
ax.set_zlim3d(-2, 2.5)
ax.set_title("2D elliptic partial differential equation")
ax.set_xlabel("X")
ax.set_ylabel("Y")
plt.show()
2.2.3 Sympy中的偏微分方程求解
虽然Sympy库不支持复杂的二阶偏微分方程的解析求解,但对于一些简单的PDE可以使用pdsolve函数。示例如下:
from sympy.solvers.pde import pdsolve
from sympy import Function, exp
from sympy.abc import x, yf = Function('f')
eq = -2 * f(x, y).diff(x) + 4 * f(x, y).diff(y) + 5 * f(x, y) - exp(x + 3 * y)
pdsolve(eq)
结果为:
Eq ( f ( x , y ) , ( F ( 4 x + 2 y ) exp ( x 2 ) + exp ( x + 4 y ) / 15 ) exp ( − y ) ) \text{Eq}(f(x, y), (F(4x + 2y) \exp(\frac{x}{2}) + \exp(x + 4y) / 15) \exp(-y)) Eq(f(x,y),(F(4x+2y)exp(2x)+exp(x+4y)/15)exp(−y))
通过上述案例和代码,我们学习了如何利用数值方法和Python库求解偏微分方程。尽管数值方法有其局限性,但在许多工程和科学问题中,这些方法是不可或缺的工具。
2.3 差分方程与元胞自动机
差分方程描述的是函数在离散点上的变化规律。与微分方程不同的是,差分方程不再是连续变量的导数,而是离散变量之间的差值。差分方程广泛应用于数值计算和计算机仿真,尤其是在工程和物理学中。
2.3.1 差分方程的基本解法
差分方程通常表示为:
y n + 1 − y n = f ( n , y n ) . y_{n+1} - y_n = f(n, y_n). yn+1−yn=f(n,yn).
这种形式称为一阶差分方程。求解这种方程的过程可以看作是迭代求解,通过逐步计算每一个离散点上的函数值来获得整个函数的值序列。
2.3.2 数值方法与数值稳定性
数值方法是解决实际工程中微分方程和差分方程的主要工具。在数值求解中,我们通常会遇到数值稳定性的问题,即数值解是否会随着迭代的进行而逐渐偏离真实解。为了确保数值解的稳定性,我们通常需要选择合适的数值方法和步长。
2.3.3 元胞自动机
元胞自动机是一种离散模型,用于模拟复杂系统的演化。它由一组规则定义,用于确定每个元胞在下一个时间步的状态。元胞自动机广泛应用于物理学、化学、生物学和计算机科学等领域。元胞自动机的基本思想是通过简单的局部规则来模拟复杂的整体行为。
2.4 实战案例
在这一节,我们将结合实际工程案例,展示微分方程和差分方程在实际问题中的应用。通过这些案例,你将更好地理解这些数学工具的实际意义,并掌握在实际工程中应用它们的方法。
案例1:人口增长模型
假设我们需要模拟一个城市的人口增长情况。我们可以使用如下的微分方程来描述人口的增长过程:
d P d t = r P ( 1 − P K ) , \frac{dP}{dt} = rP(1 - \frac{P}{K}), dtdP=rP(1−KP),
其中, P P P 是人口数量, r r r 是增长率, K K K 是环境承载能力。这个方程称为 Logistic 增长方程。我们可以使用 Python 来模拟这个过程:
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint# 定义微分方程
def logistic_growth(P, t, r, K):return r * P * (1 - P / K)# 参数设置
r = 0.1
K = 1000
P0 = 10
t = np.linspace(0, 100, 200)# 求解微分方程
P = odeint(logistic_growth, P0, t, args=(r, K))# 绘制结果
plt.plot(t, P)
plt.xlabel('时间')
plt.ylabel('人口数量')
plt.title('人口增长模型')
plt.show()
输出结果将显示人口数量随时间的变化曲线,从中可以观察到人口增长的动态过程。
案例2:热传导问题
假设我们需要模拟一个一维热传导过程。我们可以使用如下的偏微分方程来描述热传导过程:
∂ T ∂ t = α ∂ 2 T ∂ x 2 , \frac{\partial T}{\partial t} = \alpha \frac{\partial^2 T}{\partial x^2}, ∂t∂T=α∂x2∂2T,
其中, T T T 是温度, α \alpha α 是热扩散系数。我们可以使用差分方法来求解这个偏微分方程:
import numpy as np
import matplotlib.pyplot as plt# 参数设置
alpha = 0.01
L = 1.0
dx = 0.01
dt = 0.0001
nx = int(L / dx) + 1
nt = 1000# 初始条件
T = np.zeros(nx)
T[int(nx/4):int(3*nx/4)] = 1.0# 差分法求解
for n in range(nt):T_new = T.copy()for i in range(1, nx-1):T_new[i] = T[i] + alpha * dt / dx**2 * (T[i-1] - 2*T[i] + T[i+1])T = T_new# 绘制结果
x = np.linspace(0, L, nx)
plt.plot(x, T)
plt.xlabel('位置')
plt.ylabel('温度')
plt.title('热传导问题')
plt.show()
输出结果将显示温度分布随时间的变化过程,从中可以观察到热传导的动态过程。
2.5 数值计算方法与微分方程求解
在使用Python求解微分方程的数值解或函数极值时,其背后的计算方法往往与MATLAB等软件有所不同。为了更好地理解这些数值计算方法及其在微分方程求解中的应用,以下将详细探讨几种经典的数值计算方法,并结合具体案例进行说明。
2.5.1 Python通过什么求数值解
Python依赖于丰富的库来进行数值计算,其中包括NumPy、SciPy和SymPy等。这些库提供了大量的数学函数和算法,用于处理线性代数、微积分、优化问题等。例如:
- NumPy:用于高效的数组操作和基本的线性代数运算。
- SciPy:提供了数值积分、优化、线性代数、统计等高级功能。
- SymPy:用于符号计算,可以处理解析解和符号微积分等。
这些工具的组合使得Python在数值计算方面非常强大和灵活。
2.5.2 梯度下降法
梯度下降法是一种用于优化问题的常用算法,特别是在机器学习和深度学习中。其基本思想是从一个初始点出发,沿着函数梯度的反方向逐步更新变量值,直到找到函数的最小值点。
梯度下降法的迭代公式为:
[ x_{t+1} \leftarrow x_t - \alpha \cdot \text{grad}(f) ]
其中,(\alpha)是学习率,控制步长大小。
以下是使用Python实现梯度下降法求解函数极值的示例代码:
import numpy as np
import matplotlib.pyplot as plt# 定义目标函数
def func(x):return x**2 + 2*x + 5# 梯度下降法
x_iter = 1 # 初始值
learning_rate = 0.06 # 学习率
iterations = 0 # 迭代次数while True:iterations += 1y_last = func(x_iter)x_iter -= learning_rate * (2 * x_iter + 2)y_next = func(x_iter)plt.scatter(x_iter, y_last)if abs(y_next - y_last) < 1e-10:breakprint('最小值点x=', x_iter, '最小值y=', y_next, '迭代次数=', iterations)# 绘制函数曲线
x = np.linspace(-4, 6, 100)
y = func(x)
plt.plot(x, y, '--')
plt.show()
最终结果显示梯度下降法能够快速逼近函数的极小值点。
2.5.3 Newton法
Newton法(也称为切线法)是一种寻找函数零点的有效方法。其基本思想是从一个初始估计值开始,通过迭代找到零点或达到预定精度。
Newton法的迭代公式为:
[ x_{n+1} = x_n - \frac{f(x_n)}{f’(x_n)} ]
以下是使用Python实现Newton法的示例代码:
import numpy as np# 定义目标函数及其导数
def f(x):return x**3 - x - 1def f_prime(x):return 3*x**2 - 1# Newton法求解
def newton_method(x0, tol=1e-9, max_iter=100):x = x0for i in range(max_iter):x_new = x - f(x) / f_prime(x)if abs(f(x_new)) < tol:breakx = x_newreturn x, i# 初始值
x0 = 1.5
root, iterations = newton_method(x0)print(f"零点x={root}, 迭代次数={iterations}")
Newton法能够快速收敛到函数的零点,但需要注意初始值的选择对收敛速度和结果的影响。
2.5.4 Euler法与Runge-Kutta法
在数值求解微分方程时,常用的两种方法是Euler法和Runge-Kutta法。它们都基于将微分方程离散化,以近似其解。
- Euler法:通过使用当前点的斜率来估计下一点的值。
- Runge-Kutta法:通过在一个步长内使用多个斜率的平均值来估计下一点的值。
以下是使用Python实现四阶Runge-Kutta法求解微分方程的示例代码:
import numpy as np
import matplotlib.pyplot as pltdef runge_kutta(y, x, dx, f):k1 = dx * f(y, x)k2 = dx * f(y + 0.5 * k1, x + 0.5 * dx)k3 = dx * f(y + 0.5 * k2, x + 0.5 * dx)k4 = dx * f(y + k3, x + dx)return y + (k1 + 2 * k2 + 2 * k3 + k4) / 6.def func(y, t):return 1 / (1 + t**2) - 2 * y**2t = 0
y = 0
dt = 0.1
ys, ts = [0], [0]while t <= 10:y = runge_kutta(y, t, dt, func)t += dtys.append(y)ts.append(t)plt.plot(ts, ys, label='runge_kutta')
plt.legend()
plt.show()
Runge-Kutta法能够提供更高精度的数值解,适用于各种微分方程的求解。
2.5.5 Crank-Nicolson法在热传导问题中的应用
Crank-Nicolson法是一种常用于求解热传导方程和类似偏微分方程的时间推进技术。它结合了显式和隐式方法的优点,既稳定又准确。
以下是使用Python实现Crank-Nicolson法求解一维热传导方程的示例代码:
import numpy as np
import pandas as pddef crank_nicolson(T, m, n, r):A = np.eye(m-1) * (1 + r) - np.eye(m-1, k=1) * (r/2) - np.eye(m-1, k=-1) * (r/2)C = np.eye(m-1) * (1 - r) + np.eye(m-1, k=1) * (r/2) + np.eye(m-1, k=-1) * (r/2)for k in range(n):F = C @ T[k, 1:m]F[0] += (r/2) * T[k+1, 0]F[-1] += (r/2) * T[k+1, m]T[k+1, 1:m] = np.linalg.solve(A, F)return T# 初始化参数
a = 1.0
x_max, t_max = 1.0, 1.0
dx, dt = 0.1, 0.1
m, n = int(x_max / dx), int(t_max / dt)
r = a * dt / dx**2T = np.zeros((n+1, m+1))
T[:, 0] = np.exp(np.arange(0, t_max+dt, dt))
T[:, -1] = np.exp(np.arange(0, t_max+dt, dt) + 1)
T[0, :] = np.exp(np.arange(0, x_max+dx, dx))T_cn = crank_nicolson(T.copy(), m, n, r)# 输出结果
pd.DataFrame(T_cn).to_excel('crank_nicolson.xlsx')
Crank-Nicolson法能够有效地处理时间步长较大的问题,具有较高的计算稳定性和精度。
通过这些数值计算方法的介绍和具体实现,我们可以更好地理解Python在求解微分方程中的应用,并通过调整算法参数和优化代码提高计算效率和准确性。
2.6 小结
本章主要介绍了微分方程与动力系统的基本理论和应用方法。我们讨论了微分方程的基本概念、求解方法和数值方法,并结合实际案例展示了微分方程和差分方程在工程中的应用。希望通过本章的学习,你能更好地理解微分方程的基本原理,并掌握在实际工程中应用这些数学工具的方法。
相关文章:
DW学习笔记|数学建模task2
本章主要涉及到的知识点有: 微分方程的解法如何用 Python 解微分方程偏微分方程及其求解方法微分方程的基本案例差分方程的求解数值计算方法元胞自动机 2.1 微分方程的理论基础 微分方程是什么?如果你参加过高考,可能在高三备考中遇到过这…...

【大数据 复习】第9章 数据仓库分析工具Hive
一、概念 1.概述 (1)Hive是一个构建于Hadoop顶层的数据仓库工具。 (2)某种程度上可以看作是用户编程接口,本身不存储和处理数据。 (3)依赖分布式文件系统HDFS存储数据。 (4…...

ionic7 从安装 到 项目启动最后打包成 apk
报错处理 在打包的时候遇到过几个问题,这里记录下来两个 Visual Studio Code运行ionic build出错显示ionic : 无法加载文件 ionic 项目通过 android studio 打开报错 capacitor.settings.gradle 文件不存在 说明 由于之前使用的是 ionic 3,当时打包的…...

setInterval 定时任务执行时间不准验证
一般在处理定时任务的时候都使用setInterval间隔定时调用任务。 setInterval(() > {console.log("interval"); }, 2 * 1000);我们定义的是两秒执行一次,但是浏览器实际执行的间隔时间只多不少。这是由于浏览器执行 JS 是单线程模式,使用se…...
Stable Diffusion Model网站
Civitai Models | Discover Free Stable Diffusion Modelshttps://www.tjsky.net/tutorial/488https://zhuanlan.zhihu.com/p/610298913超详细的 Stable Diffusion ComfyUI 基础教程(一):安装与常用插件 - 优设网 - 学设计上优设 (uisdc.com)…...

K8S - 实现statefulset 有状态service的灰度发布
什么是灰度发布 Canary Release 参考 理解 什么是 滚动更新,蓝绿部署,灰度发布 以及它们的区别 配置partition in updateStrategy/rollingUpdate 这次我为修改了 statefulset 的1个yaml file statefulsets/stateful-nginx-without-pvc.yaml: --- apiVe…...
Qt 技术博客:深入理解 Qt 中的 delete 和 deleteLater 与信号槽机制
在 Qt 开发中,内存管理和对象生命周期的处理是至关重要的一环。特别是在涉及信号和槽机制时,如何正确删除对象会直接影响应用程序的稳定性。本文将详细讨论在使用 Qt 的信号和槽机制时,delete 和 deleteLater 的工作原理,并给出最…...

自学鸿蒙HarmonyOS的ArkTS语言<一>基本语法
一、一个ArkTs的目录结构 二、一个页面的结构 A、装饰器 Entry 装饰器 : 标记组件为入口组件,一个页面由多个自定义组件组成,但是只能有一个组件被标记 Component : 自定义组件, 仅能装饰struct关键字声明的数据结构 State:组件中的状态变量…...
【OpenGauss源码学习 —— (ALTER TABLE(列存修改列类型))】
ALTER TABLE(列存修改列类型) ATExecAlterColumnType 函数1. 检查和处理列存储表的字符集:2. 处理自动递增列的数据类型检查:3. 处理生成列的类型转换检查:4. 处理生成列的数据类型转换: build_column_defa…...

【大数据 复习】第7章 MapReduce(重中之重)
一、概念 1.MapReduce 设计就是“计算向数据靠拢”,而不是“数据向计算靠拢”,因为移动,数据需要大量的网络传输开销。 2.Hadoop MapReduce是分布式并行编程模型MapReduce的开源实现。 3.特点 (1)非共享式,…...
Zookeeper:节点
文章目录 一、节点类型二、监听器及节点删除三、创建节点四、监听节点变化五、判断节点是否存在 一、节点类型 持久(Persistent):客户端和服务器端断开连接后,创建的节点不删除。 持久化目录节点:客户端与Zookeeper断…...
生产级别的 vue
生产级别的 vue 拆分组件的标识更好的组织你的目录如何解决 props-base 设计的问题transparent component (透明组件)可减缓上述问题provide 和 inject vue-meta 在路由中的使用如何确保用户导航到某个路由自己都重新渲染?测试最佳实践如何制…...
kafka(五)spring-kafka(1)集成方法
一、集成 1、pom依赖 <!--kafka--><dependency><groupId>org.apache.kafka</groupId><artifactId>kafka-clients</artifactId></dependency><dependency><groupId>org.springframework.kafka</groupId><artif…...
Java中的设计模式深度解析
Java中的设计模式深度解析 大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿! 在软件开发领域,设计模式是一种被广泛应用的经验总结和解决方案&#x…...

鸿蒙 HarmonyOS NEXT星河版APP应用开发—上篇
一、鸿蒙开发环境搭建 DevEco Studio安装 下载 访问官网:https://developer.huawei.com/consumer/cn/deveco-studio/选择操作系统版本后并注册登录华为账号既可下载安装包 安装 建议:软件和依赖安装目录不要使用中文字符软件安装包下载完成后࿰…...

[FreeRTOS 基础知识] 互斥访问与回环队列 概念
文章目录 为什么需要互斥访问?使用队列实现互斥访问休眠和唤醒机制环形缓冲区 为什么需要互斥访问? 在裸机中,假设有两个函数(func_A, func_B)都要修改a的值(a),那么将a定义为全局变…...

音视频的Buffer处理
最近在做安卓下UVC的一个案子。正好之前搞过ST方案的开机广告,这个也是我少数最后没搞成功的项目。当时也有点客观原因,当时ST要退出机顶盒市场,所以一切的支持都停了,当时啃他家播放器几十万行的代码,而且几乎没有文档…...
【总结】攻击 AI 模型的方法
数据投毒 污染训练数据 后门攻击 通过设计隐蔽的触发器,使得模型在正常测试时无异常,而面对触发器样本时被操纵输出。后门攻击可以看作是特殊的数据投毒,但是也可以通过修改模型参数来实现 对抗样本 只对输入做微小的改动,使模型…...

Linux配置中文环境
文章目录 前言中文语言包中文输入法中文字体 前言 在Linux系统中修改为中文环境,通常涉及以下几个步骤: 中文语言包 更新源列表: 更新系统的软件源列表和语言环境设置,确保可以安装所需的语言包。 sudo apt update sudo apt ins…...

深入解析 iOS 应用启动过程:main() 函数前的四大步骤
深入解析 iOS 应用启动过程:main() 函数前的四大步骤 背景描述:使用 Objective-C 开发的 iOS 或者 MacOS 应用 在开发 iOS 应用时,我们通常会关注 main() 函数及其之后的执行逻辑,但在 main() 函数之前,系统已经为我们…...

深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录
ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...

基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...

成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战
在现代战争中,电磁频谱已成为继陆、海、空、天之后的 “第五维战场”,雷达作为电磁频谱领域的关键装备,其干扰与抗干扰能力的较量,直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器,凭借数字射…...

Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
【生成模型】视频生成论文调研
工作清单 上游应用方向:控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...

JVM 内存结构 详解
内存结构 运行时数据区: Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器: 线程私有,程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 每个线程都有一个程序计数…...

华为OD机考-机房布局
import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...
C#学习第29天:表达式树(Expression Trees)
目录 什么是表达式树? 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持: 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...

Git 3天2K星标:Datawhale 的 Happy-LLM 项目介绍(附教程)
引言 在人工智能飞速发展的今天,大语言模型(Large Language Models, LLMs)已成为技术领域的焦点。从智能写作到代码生成,LLM 的应用场景不断扩展,深刻改变了我们的工作和生活方式。然而,理解这些模型的内部…...

医疗AI模型可解释性编程研究:基于SHAP、LIME与Anchor
1 医疗树模型与可解释人工智能基础 医疗领域的人工智能应用正迅速从理论研究转向临床实践,在这一过程中,模型可解释性已成为确保AI系统被医疗专业人员接受和信任的关键因素。基于树模型的集成算法(如RandomForest、XGBoost、LightGBM)因其卓越的预测性能和相对良好的解释性…...