\[\frac{d^{2}}{d r^{2}} \phi{\left(r \right)} = - 0.5 r e^{- r}\]
IVP
给定待求函数在某个初始点上的值
\[ \left\lbrace \begin{eqnarray} &&\frac{dy}{dx} = f(x, y) \\ &&y|_{x = a} = y_0 \end{eqnarray} \right. \]
BVP
在自变量的两个端点上对待求函数施加约束,如函数值约束、导数值约束…
\[ \left\lbrace\begin{eqnarray} &&y''+\frac{\pi^2}{4}y+\frac{\pi^2}{4} = 0 \\ &&y(0) = 0, y(1) = 1 \end{eqnarray}\right. \]
EVP
一类特殊的含有参数的边值问题。只有在参数取特定值时,方程才有非零解
\[ \left\lbrace\begin{eqnarray} &&\frac{d^2\varphi(x)}{dx^2} + k^2\varphi(x) = 0 \\ &&\varphi(0) = \varphi(1) = 0 \end{eqnarray}\right. \]
BVP
在区间的两个端点上对待求函数各施加一个约束,这样方程的解就能唯一的确定
\[\Rightarrow ~~~ y(x) = \cos\frac{\pi}{2}x +2\sin\frac{\pi}{2}x-1\]
EVP
在区间的两个端点上对待求函数各施加一个约束。方程存在一个待定参数,只有当待定参数取特定值时,方程才存在非零解
\[\Rightarrow k_n = n\pi;~~\phi_n \sim \sin n\pi~~(n=1,2,...)\]
物理学中许多重要的微分方程可写成线性二阶微分方程的形式
\[ \frac{d^2y}{dx^2} + \underbrace{k^2(x)}_{\text{Real function}}y = \underbrace{S(x)}_{\text{source term}} \]
当\(k^2>0\) 时,齐次方程 (\(S = 0\))的解是振荡的,其局部波数为\(k\);当\(k^2<0\)时,齐次方程的解是指数增长或衰减的,局部变化率为 \((-k^2)^{1/2}\).
考虑求解一个定域的电荷分布\(r(r)\)所产生的静电势\(\Phi\)
Poisson 方程为 \(\nabla^2 \Phi = -4\pi\rho\)
对于这个方程,我们通常关心的是在\(r = 0\)和\(r = + \infty\)上满足某种约束条件的解(如势能在\(r = 0\)和\(r = + \infty\)处都为\(0\)),这个问题就是一个边值问题.
\[ \nabla^2\Phi = \frac{1}{r^2}\partial_r (r^2 \partial_r \Phi) + \frac{1}{r^2\sin\theta} \partial_\theta (\sin\theta \partial_\theta \Phi) + \frac{1}{r^2\sin\theta}\partial^2_\phi \Phi \]
\[ \nabla^2 \Phi = -4\pi\rho ~~~ \xrightarrow{\text{for spherical symmetrical }r\text{ and }\Phi} ~~~ \frac{1}{r^2}\frac{d}{dr}\left[r^2\frac{d\Phi}{dr}\right] \]
substitute \(\Phi(r) = r^{-1}\phi(r) \longrightarrow \frac{d^2\phi}{dr^2} = -4\pi r \rho\)
i.e. \(\frac{d^2y}{dx^2} + k^2(x)y = S(x) \longrightarrow k^2 = 0, ~~ S = -4\pi r \rho\).
If \(\rho(r) = \frac{1}{8\pi}e^{-r} \longrightarrow \frac{d^2\phi}{dr^2} = -\frac{1}{2}re^{-r} \longrightarrow \phi(r) = 1 - \frac{1}{2}(r+2)e^{-r}\).
\[\frac{d^{2}}{d r^{2}} \phi{\left(r \right)} = - 0.5 r e^{- r}\]
\[\phi{\left(r \right)} = - 0.5 r e^{- r} + 1 - 1.0 e^{- r}\]
量子力学中,一个质量为\(m\),能量为\(E\)的粒子在中心位势\(V(r)\)中运动,其波函数满足如下的薛定谔方程 \[ \left[-\frac{\hbar^2}{2m}\nabla^2 + V(r)\right]\Psi = E\Psi \] \(\longrightarrow\) \[ \Psi(r, \theta, \phi) = r^{-1}R(r)Y_{lm}(\theta, \phi) \]
\[ -\frac{\hbar^2}{2m}\left[ \frac{1}{r^2}\partial_r (r^2 \partial_r) + \frac{1}{r^2\sin\theta} \partial_\theta (\sin\theta \partial_\theta) + \frac{1}{r^2\sin\theta}\partial^2_\phi \right] = (E-V(r))\Psi \]
Radial wavefunction \(R\): \(\frac{d^2R}{dr^2} + \underbrace{\frac{2m}{\hbar} \left[ E - \frac{l(l+1)\hbar^2}{2mr^2} - V(r) \right]}_{k^2(r)}R = \underbrace{0}_{S = 0} \leftrightarrow \frac{d^2y}{dx^2} + k^2(x)y = S(x)\)
对于该方程,我们感兴趣的是对哪些能量本征值
对于上述两类问题:
针对常微分方程 \(\frac{d^2y}{dx^2} + k^2(x)y = S(x)\) 的一种特别有效的算法。
二阶导数的3点差分公式 \(f''(x_0) = \frac{f(x_0 + h) - 2f(x_0) + f(x_0 - h)}{h^2} + O(h^2)\)
首先用二阶导数的3点差分公式 \(f'' = \frac{f_{+1} - 2f_0 + f_{-1}}{h^2}\) 以及方程 \[\frac{d^2y}{dx^2} + k^2(x)y = S(x)\]
求解 \(y\) 的四阶导数:
\[ \begin{eqnarray} y_n'''' &=& \frac{d^2}{dx^2}(-k^2y + S)|_{x = x_n } \\ &=& -\frac{(k^2y)_{n+1} - 2(k^2y)_n + (k^2y)_{n-1}}{h^2} + \frac{S_{n+1} - 2S_n + S_{n-1}}{h^2} + O(h^2) \end{eqnarray} \]
\[ f_{\pm 1} \equiv f(\pm h) = f_0 \pm hf' + \frac{h^2}{2}f'' \pm \frac{h^3}{6}f''' + \frac{h^4}{24}f^{(4)} \pm \frac{h^5}{5!}f^{(5)} + O(h^6) \]
\[ f_{+1} + f_{-1} = 2f_0 + h^2f'' + \frac{h^4}{12}f^{(4)} + O(h^6) \]
\[ \Rightarrow \frac{f_{+1} - 2f_0 + f_{-1}}{h^2} = f'' + \frac{h^2}{12}f^{(4)} + O(h^4) \]
\[ \Rightarrow \frac{y_{n+1} -2y_n + y_{n-1}}{h^2} = y'' + \frac{h^2}{12}y_n^{(4)} + O(h^4) \]
\[\begin{eqnarray} \Rightarrow \frac{y_{n+1} -2y_n + y_{n-1}}{h^2} = (S_n - k_n^2 y_n) &-& \frac{k^2_{n+1} y_{n+1} - 2k_n^2y_n + k^2_{n-1}y_{n-1}}{12} \\ &+& \frac{S_{n+1} - 2S_n + S_{n-1}}{12} + O(h^4) \end{eqnarray} \]
Numerov method
按照\(y\)的同类项整理可得
\[\begin{eqnarray} \left( 1 + \frac{h^2}{12}k_{n+1}^2 \right)y_{n+1} - 2\left( 1 - \frac{5h^2}{12}k_n^2 \right)y_n &+& \left( 1 + \frac{h^2}{12}k_{n-1}^2 \right)y_{n-1} \\ &=& \frac{h^2}{12}(S_{n+1} + 10S_n + S_{n-1}) + O(h^6) \end{eqnarray}\]这是一个关于\(y_{n+1}\) 或者 \(y_{n-1}\) 的线性方程。对 \(y_{n+1}\) 或者 \(y_{n-1}\) 解此线性方程,就提供了一个对 \(x\) 向前或向后的递推关系,其局部截断误差为 \(O(h^6)\),其精度比四阶RK方法还要高一阶。
我们可以用
Exercise 1 \[ \frac{d^2y}{dx^2} = -4\pi^2y ~~~ y(0) = 1; ~~~ y'(0) = 0; ~~~ y = \cos 2\pi x; \] 用不同的步长 \(h\),从 \(x = 0\) 积分到 \(x = 1\),比较Numerov算法与经典四阶R-K算法的效率和精度。
Solution 1. \[ S_i^2 = 4\pi^2;~~~ S_i \equiv 0; ~~~ y_{n+1} = \frac{2(3-5\pi^2h^2)}{(3+\pi^2h^2)}y_n - y_{n-1}; \]
\[ y_0 = y(0) = 1; ~~~ y_1 = ? \]
Taylor expansion to obtain \(y_1\)
\[\frac{h^{2} \left. \frac{d^{2}}{d \xi^{2}} y{\left(\xi \right)} \right|_{\substack{ \xi=0 }}}{2} + h \left. \frac{d}{d \xi} y{\left(\xi \right)} \right|_{\substack{ \xi=0 }} + y{\left(0 \right)}\]
\[ y_1 \approx y_0 + hy_0 + \frac{h^2}{2}y_0'' = 1 - 2\pi^2h^2 \]
RK: reduce the order:
\[ \left\lbrace\begin{align} &\frac{d^2y}{dx^2} = -4\pi^2y \\ &y(0) =1\\ &y'(0) = 0 \end{align}\right.\Longrightarrow \left\lbrace\begin{aligned}[l] &\frac{dy}{dx} = z \\ &\frac{dz}{dx} = -4\pi^2 y \\ &y(0) = 1\\ &z(0) = 0 \end{aligned}\right. \]
def ode(x, y):
res = y.copy()
res[0] = y[1]
res[1] = -4*np.pi**2 * y[0]
return res
def rk4(func, x0, y0, h, xp):
steps = int((xp - x0) / h)
result = np.zeros((steps + 1, len(y0)))
xis = np.zeros((steps + 1, 1))
xi = x0
yi = y0
xis[0] = x0
result[0, :] = yi
for i in range(steps):
k1 = func(xi, yi)
k2 = func(xi + 0.5 * h, yi + 0.5 * h * k1)
k3 = func(xi + 0.5 * h, yi + 0.5 * h * k2)
k4 = func(xi + h, yi + h * k3)
xi = xi + h
yi = yi + h / 6.0 * (k1 + 2.0 * k2 + 2.0 * k3 + k4)
xis[i + 1] = xi
result[i + 1, :] = yi
return xis, result
h = 0.01
x0 = 0
xp = 1.0
y0 = np.array([1.0, 0.0])
xi, yi = rk4(ode, x0, y0, h, xp)
plt.plot(xi, yi[:, 0], label = r'$y$')
plt.plot(xi, yi[:, 1], label = r'$dy/dt$')
plt.xlabel(r'$t$')
plt.legend()
plt.show()
def numerov(x0, y0, y0p, h, xp):
steps = int((xp - x0) / h)
result = np.zeros((steps + 1, 1))
xis = np.zeros((steps + 1, 1))
y1 = y0 + h * y0p + (- 4.0 * np.pi**2.0 * y0) * h**2.0 / 2.0
result[1] = y1
result[0] = y0
fac = 2.0 * (3.0 - 5.0 * np.pi**2.0 * h**2.0) / (3.0 + np.pi**2.0 * h**2.0)
xis[0] = x0
xis[1] = x0 + h
for i in range(steps - 1):
j = i + 1
xis[j + 1] = (j + 1) * h + x0
result[j + 1] = fac * result[j] - result[j - 1]
return xis, result
def numerov2(x0, y0, y1, h, xp):
steps = int((xp - x0) / h)
result = np.zeros((steps + 1, 1))
xis = np.zeros((steps + 1, 1))
result[1] = y1
result[0] = y0
fac = 2.0 * (3.0 - 5.0 * np.pi**2.0 * h**2.0) / (3.0 + np.pi**2.0 * h**2.0)
xis[0] = x0
xis[1] = x0 + h
for i in range(steps - 1):
j = i + 1
xis[j + 1] = (j + 1) * h + x0
result[j + 1] = fac * result[j] - result[j - 1]
return xis, result
x0 = 0
y0 = 1
y0p = 0
h = 0.01
xp = 1
xis, result = numerov(x0, y0, y0p, h, xp)
xis2, result2 = numerov2(x0, y0, yi[1, 0], h, xp)
ex_y = np.cos(2.0 * np.pi * xi[:, 0])
plt.plot(xi, ex_y, label = 'explicit')
plt.plot(xis, result, 'r--', label = 'numerov')
plt.plot(xi, yi[:, 0], 'b-.', label = 'rk4')
plt.xlabel('x')
plt.ylabel('y')
plt.legend()
plt.show()
%filename: chapter3_example_0_Numerov.m
%比较Numerov方法与四阶R-K算法
function compare_RK_Numerov
clear all
close all
clc
format long
A=1;
x=0:0.001:A;
y=@(x)cos(2*pi*x); %方程的精确解
h=0.01;
N=fix(A/h); %fix表示取整运算
xx=0:h:N*h;
%-----------四阶R-K算法-------------------
y_rk=zeros(1,N+1);
z_rk=zeros(1,N+1);
tic
f1=@(xx,yy)[yy(2),-4*pi^2*yy(1)]; % 定义 f1,
z0 = [1,0];
z1 = myrungekutta(f1, xx, z0);
y_rk = z1(:,1)'; %用四阶RK算法求得的y值
z_rk = z1(:,2)';
toc
%------Numerov算法------------
tic
y_n(1)=1;
y_n(2)=y_rk(2);%cos(2*pi*h);%cos(2*pi*h); %y_rk(2);%1-2*pi^2*h^2;%cos(2*pi*h);
% 1-2*pi^2*h^2 %Numerov算法的精度和启动点的精度有关,
%如采用1-2*pi^2*h^2时精度就和RK算法差不多
for n=2:N
y_n(n+1)=2*(3-5*pi^2*h^2)/(3+pi^2*h^2)*y_n(n)-y_n(n-1);
end
toc
a=0.9;
n=round(a/h);
fprintf('y(a)的精确值为:%f\n',y(a));
fprintf('四阶R-K法求得的值为:%f\n',y_rk(n+1));
fprintf('四阶R-K法的误差为:%f\n',y(a)-y_rk(n+1));
fprintf('Numerov法求得的值为:%f\n',y_n(n+1))
fprintf('Numerov法的误差为:%f\n',y(a)-y_n(n+1));
figure;plot(x,y(x),'b',xx,y_rk,'r',xx,y_n,'k')%精确解,RK算法,Numerov算法
legend('精确解','RK','Numerov')
figure;plot(xx,y_rk-y(xx),'r',xx,y_n-y(xx),'b') %RK算法与Numerov算法误差比较图
legend('RK','Numerov')
title('RK算法与Numerov算法误差比较图')
function z = myrungekutta(f, xx, z)
n = length(xx);
h = xx(2)-xx(1);
for i = 1:n-1
k1 = f(xx(i), z(i,:));
k2=f(xx(i)+0.5*h, z(i,:) + h*k1/2);
k3=f(xx(i)+0.5*h, z(i,:) + h*k2/2);
k4=f(xx(i)+h, z(i,:) + h*k3);
z(i+1,:)=z(i,:) + (k1 + 2*k2 + 2*k3 + k4)*h/6;
end
Note
Numerov算法只针对特定二阶常微分方程,使用上有局限性。
本节介绍求解两点边值问题 \[ \left\lbrace \begin{align} &y'' = f(x, y, y') \\ &y(a) = \alpha, y(b) = \beta, \end{align}\right. \qquad(1)\] 的数值解法。
当 \(f\) 关于 \(y\) 和 \(y'\) 是线性时,式(Equation 1)为线性两点边值问题 \[ \left\lbrace \begin{align} &y'' + p(x)y' + q(x)y = f(x) \\ &y(a) = \alpha, y(b) = \beta, \end{align} \right. \qquad(2)\]
对于线性边值问题(Equation 2),一个简单又实用的方法是用解析的思想,将它转化为两个初值问题:
\[ \left\lbrace \begin{aligned} &y'' + p(x)y' + q(x)y = f(x), \\ &y(a) = \alpha, y(b) = \beta, \end{aligned} \right. ~ \Rightarrow ~ \left\lbrace \begin{aligned} &y_1'' + p(x)y_1' + q(x)y_1 = f(x), \\ &y_1(a) = \alpha, y_1'(a) = 0, \end{aligned} \right.\\ \left\lbrace \begin{aligned} &y_2'' + p(x)y_2' + q(x)y_2 = 0, \\ &y_2(a) = 0, y'_2(a) = 1, \end{aligned} \right. \] 求得这两个初值问题的解 \(y_1(x)\) 和 \(y_2(x)\) , 若 \(y_2(b)\neq 0\),容易验证
\[
y(x) = y_1(x) + \frac{\beta - y_1(b)}{y_2(b)}y_2(x)
\qquad(3)\] 为线性两点边值问题(Equation 2)的解,此种方法称为
对于非线性边值问题,迭加法无能为力。打靶法的基本原理是将两点边值问题(1)转化为下列形式的初值问题
\[ \left\lbrace \begin{aligned}[l] &y'' = f(x, y, y'), \\ &y(a) = \alpha, ~~ y'(a) = s_k \end{aligned} \right. \qquad(4)\]
这里的 \(s_k\) 为 \(y\) 在 \(a\) 处的斜率。令 \(z = y'\) ,上述二阶方程可降为一阶方程组 \[
\left\lbrace
\begin{aligned}[l]
&y' = z, \\
&z' = f(x, y, z), \\
&y(a) = \alpha, ~~ z(a) = s_k
\end{aligned}
\right.
\qquad(5)\] 因此,
❓ 问题的关键是如何去找合适的初始斜率的试探值?
对给定的 \(s_k\) ,设初值问题(Equation 4)的解为\(y(x,s_k)\),它是\(s_k\)的隐函数。假设\(y(x, s_k)\)随\(s_k\)是连续变化的,记为\(y(x, s)\),于是我们要找的\(s_k\)就是方程 \[ \underbrace{y(b, s) - \beta}_\text{roots function} = 0 \] 的根。可以用第2章的弦割法求上述方程的根。 \[ x_k = x_{k-1} - \frac{F(x_{k-1})(x_{k-1} - x_{k-2})}{F(x_{k-1}) - F(x_{k - 2})} \]
\[ s_k = s_{k-1} - \frac{s_{k - 1} - s_{k - 2}}{y(b, s_{k - 1}) - y(b, s_{k - 2})}[y(b, s_{k - 1}) - \beta], ~~ k = 2, 3, ... \qquad(6)\]
这样,可以按下面简单的计算过程进行求解:
如此重复,直到某个\(s_k\)满足\(|y(b, s_k) - \beta| < \varepsilon\),此时得到的\(y(x_i)\)和\(y_i' = z(x_i)\)就是边值问题的解函数值和它的一阶导数值。上述方程好比打靶,\(s_k\)作为斜率为子弹的发射,\(y(b)\)为靶心,故称为
打靶法的算法实现
S1
;y(b,S1)
;S2
作为弦割法的第二个启动点;y(b,S2)
;IF (abs(y(b,S1)-y(b))<精度
\(\varepsilon\) ), y(x)=y(x,S1)
;IF (abs(y(b,S2)-y(b))<精度
\(\varepsilon\) ), y(x)=y(x,S2); else
S3
;y(b,S3)
;IF (abs(y(b,S3)-y(b))<精度
\(\varepsilon\) ), y(x)=y(x,S3); else S1=S2; S2=S3
;回到第7步y(x)=y(x,S3)
;Example 1 用
\[ \left\lbrace \begin{aligned}[l] &y'' + xy' - 4y = 12x^2 - 3x, ~~ 0<x<1, \\ &y(0) = 0, ~~ y(1) = 2 \end{aligned} \right. \] 其解的解析表达式为\(y(x) = x^4 + x\)。
Solution
\[ \begin{aligned}[l] \left\lbrace \begin{aligned}[l] &y_1'' + xy_1' -4 y_1 = 12x^2 - 3x, \\ &y_1(0) = 0, ~~ y_1'(0) = 0, \end{aligned} \right.\\ \left\lbrace \begin{aligned}[l] &y_2'' + xy_2' -4y_2 = 0, \\ &y_2(0) = 0, ~~ y_2'(0) = 1 \end{aligned} \right. \end{aligned} \]
let \(z_1 = y_1', z_2 = y_2'\), 将上述两个边值问题分别降为一阶方程组初值问题
\[ \begin{aligned}[l] \left\lbrace \begin{aligned}[l] &y_1' = z_1, \\ &z_1' = -xz_1 + 4y_1 + 12x^2 - 3x, \\ &y_1(0) = 0, ~~ z_1(0) = 0 \end{aligned} \right. \\ \left\lbrace \begin{aligned}[l] &y_2' = z_2, \\ &z_2' = -xz_2 + 4y_2, \\ &y_2(0) = 0, ~~ z_2(0) = 1 \end{aligned} \right. \end{aligned} \]
Refer (Equation 3) with \(b = 1, ~~\beta = 2\)
取 \(h = 0.02\),用经典R-K法分别求这两个方程组解\(y_1(x)\)和\(y_2(x)\)的计算值\(y_{1i}\)和\(y_{2i}\),然后按(Equation 3)得精确解 \[ y(x) = y_1(x) + \frac{2 - y_1(1)}{y_2(1)}y_2(x)\]
Code:
%filename: chapter3_example_1_superposition_method.m
%用迭加法求解线性常微分方程的边值问题,其中用到了四阶R-K算法
function superposition_method
clear
clc,close all
format long
A=1;
x=0:0.001:A;
y=@(x)(x.^4+x); %方程的精确解
XL=0.0; %左边界点
XU=1.0; %右边界点
h=0.02;
N=(XU-XL)/h;
xx=0:h:N*h;
YL= 0.0; %边界点函数值
% y_rk1=zeros(1,N+1);
% z_rk1=zeros(1,N+1);
%
% y_rk2=zeros(1,N+1);
% z_rk2=zeros(1,N+1);
%
% yy_rk=zeros(1,N+1);
%-----------四阶R-K算法-------------------
f1=@(xx,yy)[yy(2),-xx*yy(2)+4*yy(1)+12*xx^2-3*xx]; % 定义 f1,
z1 = [0,0]; %第一个常微分方程组的初值
z1 = myrungekutta(f1, xx, z1);
y_rk1 = z1(:,1); %用四阶RK算法求得的y值
z_rk1 = z1(:,2);
%----------第二个方程组的解,求右边界的函数值及误差函数--------------------
f2=@(xx,yy)[yy(2),-xx*yy(2)+4*yy(1)]; % 定义 f1,
z2 = [0,1]; %第二个常微分方程组的初值
z2 = myrungekutta(f2, xx, z2);
y_rk2 = z2(:,1); %用四阶RK算法求得的y值
z_rk2 = z2(:,2);
yy_rk=y_rk1+y_rk2*(2-y_rk1(end))/y_rk2(length(xx));
for a=0:0.2:1.0;
n=round(a/h);
error1(n+1)=y(a)-yy_rk(n+1);
fprintf('%11.10f\t',a,y_rk1(n+1),y_rk2(n+1),yy_rk(n+1),y(a),abs(error1(n+1)));
fprintf('\n');
end
for a=0:h:N*h;
n=round(a/h);
error(n+1)=y(a)-yy_rk(n+1);
end
fprintf('迭加法求得y(1)的值为:%f\n',yy_rk(N+1));
fprintf('迭加法求得y(1)值的误差为:%f\n',y(1)-yy_rk(N+1));
subplot(2,1,1),plot(x,y(x),'b',xx,yy_rk,'r')%精确解
title('迭加法结果与精确解比较')
legend('精确解','迭加法','Location','best')
subplot(2,1,2),plot(xx,error,'r') %打靶法误差曲线
title('迭加法误差曲线')
hold off
function z = myrungekutta(f, xx, z)
n = length(xx);
h = xx(2)-xx(1);
for i = 1:n-1
k1 = f(xx(i), z(i,:));
k2=f(xx(i)+0.5*h, z(i,:) + h*k1/2);
k3=f(xx(i)+0.5*h, z(i,:) + h*k2/2);
k4=f(xx(i)+h, z(i,:) + h*k3);
z(i+1,:)=z(i,:) + (k1 + 2*k2 + 2*k3 + k4)*h/6;
end
def ode1(x, y):
res = y.copy()
res[0] = y[1]
res[1] = -x * y[1] + 4 * y[0] + 12 * x**2 - 3 * x
return res
def ode2(x, y):
res = y.copy()
res[0] = y[1]
res[1] = - x * y[1] + 4 * y[0]
return res
y00 = np.array([0, 0])
y01 = np.array([0, 1])
h = 0.02
x0 = 0
xp = 1
xi1, yi1 = rk4(ode1, x0, y00, h, xp)
xi2, yi2 = rk4(ode2, x0, y01, h, xp)
y1 = yi1[:, 0]
y2 = yi2[:, 0]
yfin = y1 + (2.0 - y1[-1]) / y2[-1] * y2
y_ex = xi1**4.0 + xi1
plt.plot(xi1, yfin, 'r-', label = 'superstition')
plt.plot(xi1, y_ex, 'b-.', label = 'explicit')
plt.xlabel('x')
plt.ylabel('y')
plt.legend()
plt.show()
Shooting Method: (Example 1) (Equation 6)
Code:
%filename: chapter3_example_1_shooting_method.m
%用打靶法求解常微分方程的边值问题,其中用到了弦割法和四阶R-K算法;
function shooting_method_RK_secant
clear
clc
close all
format long
A=1;
x=0:0.001:A;
y=@(x)(x.^4+x); %方程的精确解
DL=5*10^-27; %误差判断标准,用于弦割法
XL=0.0; %左边界点
XU=1.0; %右边界点
h=0.02;
N=(XU-XL)/h;
xx=XL:h:XL+N*h;
%D = 1; %初始化误差
YL= 0.0; %左边界点函数值
YU= 2.0; %右边界点函数值
S0= 1000; %XL处斜率的猜测值,弦割法第一个启动点
S1= 4; %第二个斜率猜测值,作为弦割法的第二个启动点
%-----------四阶R-K算法,将求解放在循环外部-------------------
%---------第一个试探解,求右边界的函数值及误差函数-------------------
f=@(xx,yy)[yy(2);-xx*yy(2)+4*yy(1)+12*xx^2-3*xx]; % 定义 f1,
z1 = [YL;S0];
z1 = myrungekutta(f, xx, z1);
y_rk1 = z1(1,:)'; %用四阶RK算法求得的y值
z_rk1 = z1(2,:)';
F0=y_rk1(end)-YU; %用四阶RK算法得到的XU点的函数值与边界条件的差值
%----------第二个试探解,求右边界的函数值及误差函数--------------------
z2 = [YL;S1];
z2 = myrungekutta(f, xx, z2);
y_rk2 = z2(1,:)'; %用四阶RK算法求得的y值
z_rk2 = z2(2,:)';
F1=y_rk2(end)-YU; %用四阶RK算法得到的XU点的函数值与边界条件的差值
if (abs(F1)>abs(F0))
y_rk=y_rk1;
else
y_rk=y_rk2;
end
nn=0; %迭代次数
%abs(y_rk(end)-YU)
while(abs(y_rk(end)-YU)>DL)
nn=nn+1
S2=S1-F1*(S1-S0)/(F1-F0); %弦割法求根
S0=S1;
S1=S2;
F0=F1;
y_rk1=y_rk;
%----------第三或更高次的试探解,求右边界的函数值及误差函数--------------------
z2 = [YL;S1];
z2 = myrungekutta(f, xx, z2);
y_rk2 = z2(1,:)'; %用四阶RK算法求得的y值
z_rk2 = z2(2,:)';
F1=y_rk2(end)-YU %用四阶RK算法得到的XU点的函数值与边界条件的差值
if (abs(F1)>abs(F0))
y_rk=y_rk1;
else
y_rk=y_rk2;
end
%abs(y_rk(end)-YU)
end
for a=XL:h:XL+N*h;
n=round((a-XL)/h);
error(n+1)=y(a)-y_rk(n+1);
end
fprintf('打靶法求得y(1)的值为:%f\n',y_rk(N+1));
fprintf('打靶法求得y(1)值的误差为:%f\n',y(1)-y_rk(N+1));
fprintf('打靶法的迭代次数为:%f\n',nn);
subplot(2,1,1),plot(x,y(x),'b',xx,y_rk,'r')%精确解与数值解
title('打靶法结果与精确解比较')
legend('精确解','打靶法','Location','best')
subplot(2,1,2),plot(xx,error,'r') %打靶法误差曲线
title('打靶法误差曲线')
hold off
%vpa(error(end),28)
function z = myrungekutta(f, xx, z)
n = length(xx);
h = xx(2)-xx(1);
for i = 1:n-1
k1 = f(xx(i), z(:,i));
k2=f(xx(i)+0.5*h, z(:,i) + h*k1/2);
k3=f(xx(i)+0.5*h, z(:,i) + h*k2/2);
k4=f(xx(i)+h, z(:,i) + h*k3);
z(:,i+1)=z(:,i) + (k1 + 2*k2 + 2*k3 + k4)*h/6;
end
def shooting(ode, x0, y0, h, xp, yp, eps):
s1 = 0.1
s2 = 0.2
y00 = np.array([y0, 0.0])
while true:
y00[1] = s1
x1, y1 = rk4(ode, x0, y00, h, xp)
y00[1] = s2
x2, y2 = rk4(ode, x0, y00, h, xp)
y11 = y1[:, 0]
y22 = y2[:, 0]
if (np.abs(y11[-1] - yp) < eps):
return x1, y11
if (np.abs(y22[-1] - yp) < eps):
return x2, y22
s3 = s2 - (s2 - s1) / (y22[-1] - y11[-1]) * (y22[-1] - yp)
print(s3)
y00[1] = s3
x3, y3 = rk4(ode, x0, y00, h, xp)
y33 = y3[:, 0]
if (np.abs(y33[-1] - yp) < eps):
return x3, y33
s1 = s2
s2 = s3
def ode(x, y):
res = y.copy()
res[0] = y[1]
res[1] = -x * y[1] + 4 * y[0] + 12 * x**2 - 3 * x
return res
x0 = 0
xp = 1
y0 = 0
yp = 2
h = 0.02
eps = 1e-6
xx, yy = shooting(ode, x0, y0, h, xp, yp, eps)
plt.plot(xx, yy)
plt.xlabel('x')
plt.ylabel('y')
plt.show()
1.0000000334999262
Example 2 我们考虑电荷分布为 \(\rho(r) = \frac{1}{8\pi}e^{-r}\) 的电势分布 \[ Q = \int \rho(r)dr^3 = \int_0^\infty p(r)4\pi r^2dr = 1\] 此问题的精确解为 \(\Phi(r\rightarrow \infty) = r^{-1}\phi(r\rightarrow\infty) = 0\)
\[\phi(r) = 1-\frac{1}{2}(r+2)e^{-r}\]
\[ \frac{d^2\phi}{dr^2} = -4\pi r \rho = -\frac{1}{2}re^{-r} \]
利用打靶法求解 \[ \left\lbrace \begin{aligned}[l] &\frac{d^2\phi}{dr^2} = -\frac{1}{2}re^{-r} \\ &\phi(0) = 0, ~~ \phi(\infty) = 1 \end{aligned} \right. \longrightarrow \left\lbrace \begin{aligned}[l] &\phi' = z \\ &z' = -\frac{1}{2}re^{-r} \\ &\phi(0) = 0, ~~ z(0) = S \end{aligned} \right. \]
%filename: chapter3_example_2_shooting_method_poission.m
%用打靶法求解Poisson方程的边值问题,其中用到了弦割法和四阶R-K算法;
function shooting_method_RK_secant
clear
clc,close all
format long
A=20;
x=0:0.001:A;
y=@(x)(1-0.5*(x+2).*exp(-x)); %方程的精确解
DL=0.5*10^-10; %误差判断标准,用于弦割法
XL=0.0; %左边界点
XU=A; %右边界点
h=0.02;
N=(XU-XL)/h;
xx=XL:h:XL+N*h;
YL= 0.0; %左边界点函数值
YU= 1.0; %右边界点函数值
S0= 350; %XL处斜率的猜测值,弦割法第一个启动点
S1= -4500; %第二个斜率猜测值,作为弦割法的第二个启动点
% y_rk1=zeros(1,N+1);
% z_rk1=zeros(1,N+1);
% y_rk2=zeros(1,N+1);
% z_rk2=zeros(1,N+1);
% y_rk=zeros(1,N+1);
% z_rk=zeros(1,N+1);
%-----------四阶R-K算法,将求解放在循环外部-------------------
%---------第一个试探解,求右边界的函数值及误差函数-------------------
f=@(xx,yy)[yy(2),-0.5*xx*exp(-xx)]; % 定义 f1, % 定义 f1,
z1 = [YL,S0];
z1 = myrungekutta(f, xx, z1);
y_rk1 = z1(:,1); %用四阶RK算法求得的y值
% z_rk1 = z1(:,2);
F0=y_rk1(end)-YU; %用四阶RK算法得到的XU点的函数值与边界条件的差值
%----------第二个试探解,求右边界的函数值及误差函数--------------------
z2 = [YL,S1];
z2 = myrungekutta(f, xx, z2);
y_rk2 = z2(:,1); %用四阶RK算法求得的y值
% z_rk2 = z2(:,2);
F1=y_rk2(end)-YU; %用四阶RK算法得到的XU点的函数值与边界条件的差值
if (abs(F1)>abs(F0))
y_rk=y_rk1;
else
y_rk=y_rk2;
end
nn=0;
while(abs(y_rk(end)-YU)>DL)
nn=nn+1;
S2=S1-F1*(S1-S0)/(F1-F0); %弦割法求根
S0=S1;
S1=S2;
F0=F1;
y_rk1=y_rk;
%----------第三或更高次的试探解,求右边界的函数值及误差函数--------------------
z2 = [YL,S1];
z2 = myrungekutta(f, xx, z2);
y_rk2 = z2(:,1); %用四阶RK算法求得的y值
% z_rk2 = z2(:,2);
F1=y_rk2(end)-YU; %用四阶RK算法得到的XU点的函数值与边界条件的差值
if (abs(F1)>abs(F0))
y_rk=y_rk1;
else
y_rk=y_rk2;
end
%abs(y_rk(end)-YU)
end
for a=XL:h:XL+N*h;
n=round((a-XL)/h);
error(n+1)=y(a)-y_rk(n+1);
end
fprintf('打靶法求得y(100)的值为:%f\n',y_rk(N+1));
fprintf('打靶法求得y(100)值的误差为:%f\n',y(A)-y_rk(N+1));
fprintf('打靶法的迭代次数为:%f\n',nn);
subplot(2,1,1),plot(x,y(x),'b',xx,y_rk,'r')%精确解
title('打靶法结果与精确解比较')
legend('精确解','打靶法','Location','best')
subplot(2,1,2),plot(xx,error,'r') %打靶法误差曲线
title('打靶法误差曲线')
hold off
function z = myrungekutta(f, xx, z)
n = length(xx);
h = xx(2)-xx(1);
for i = 1:n-1
k1 = f(xx(i), z(i,:));
k2=f(xx(i)+0.5*h, z(i,:) + h*k1/2);
k3=f(xx(i)+0.5*h, z(i,:) + h*k2/2);
k4=f(xx(i)+h, z(i,:) + h*k3);
z(i+1,:)=z(i,:) + (k1 + 2*k2 + 2*k3 + k4)*h/6;
end
常微分方程具有如下常见的三种边界条件:
第一类
\[ \left\lbrace \begin{aligned}[l] &y'' = f(x, y, y') \\ &y(a) = \alpha, ~~ y'(a) = s_k \end{aligned} \right. \]
第二类
\[ \left\lbrace \begin{aligned}[l] &y'' = f(x, y, y') \\ &y(a) = s_k, ~~ y'(a) = \alpha \end{aligned} \right. \]
第三类
仍可转化为考虑初值问题,取\(y'_0 = \beta_0 + \alpha_0 y_0\),以 \(y_0\)为待定参数。打靶法如下: \[ \left\lbrace \begin{aligned}[l] &y'' = f(x, y, y') \\ &y(a) = y_0, ~~ y'_0 = \beta_0 + \alpha_0 y_0, \\ &y'(b) = \frac{y(b) - y(b - h)}{h} = \alpha_1 y(b) + \beta_1 \end{aligned} \right. \]
Example 3 \[ \left\lbrace \begin{aligned}[l] &y'' + xy' -4y = 12x^2 - 3x, ~~ 0<x<1, \\ &y'(0) = 1, ~~ y'(1) = 5 \end{aligned} \right. \] sol: \(y(x) = x^4 +x\).
Solution:
解:对方程做降阶处理:
\[ \left\lbrace \begin{aligned}[l] &y' = z; \\ &z' = -xz + 4y + 12x^2 - 3x, \\ &y(0) = S, ~~ z(0) = 1, \end{aligned} \right. \]
%filename: chapter3_example_3_shooting_method.m
%用打靶法求解常微分方程的边值问题(第二类边界条件),其中用到了弦割法和四阶R-K算法。
function shooting_method_RK_secant
clear
clc
close all
format long
A=1;
x=0:0.001:A;
y=@(x)(x.^4+x); %方程的精确解;
DL=10^-15; %误差判断标准,用于弦割法
XL=0.0; %左边界点
XU=1.0; %右边界点
h=0.02;
N=(XU-XL)/h;
xx=0:h:N*h;
YL_D= 1.0; %边界点导数值
YU_D= 5.0; %边界点导数值
S0= 1000; %XL处函数猜测值,弦割法第一个启动点
S1= 2000; %第二个函数猜测值,作为弦割法的第二个启动点
% y_rk1=zeros(1,N+1);
% z_rk1=zeros(1,N+1);
% y_rk2=zeros(1,N+1);
% z_rk2=zeros(1,N+1);
% y_rk=zeros(1,N+1);
% z_rk=zeros(1,N+1);
%---------第一个试探解,求右边界的函数值及误差函数-------------------
f=@(xx,yy)[yy(2),-xx*yy(2)+4*yy(1)+12*xx^2-3*xx]; % 定义 f1,
z10 = [S0,YL_D];
z1 = myrungekutta(f, xx, z10);
y_rk1 = z1(:,1); %用四阶RK算法求得的y值
z_rk1 = z1(:,2);
F0=z_rk1(end)-YU_D; %用四阶RK算法得到的XU点的导数值与边界条件的差值
%----------第二个试探解,求右边界的函数值及误差函数--------------------
z20 = [S1,YL_D];
z2 = myrungekutta(f, xx, z20);
y_rk2 = z2(:,1); %用四阶RK算法求得的y值
z_rk2 = z2(:,2);
F1=z_rk2(end)-YU_D; %用四阶RK算法得到的XU点的导数值与边界条件的差值
D=min(abs(F1),abs(F0));
nn=0;
while(D>DL)
S2=S1-F1*(S1-S0)/(F1-F0);
S0=S1;
S1=S2;
F0=F1;
nn=nn+1;
%----------第三或更高次的试探解,求右边界的函数值及误差函数--------------------
z20 = [S1,YL_D];
z2 = myrungekutta(f, xx, z20);
y_rk2 = z2(:,1); %用四阶RK算法求得的y值
z_rk2 = z2(:,2);
F1=z_rk2(end)-YU_D; %用四阶RK算法得到的XU点的导数值与边界条件的差值
D=min(abs(F1),abs(F0));
end
if (abs(F1)>abs(F0))
y_rk=y_rk1;
z_rk=z_rk1;
else
y_rk=y_rk2;
z_rk=z_rk2;
end
for a=0:h:N*h;
n=round(a/h);
error(n+1)=y(a)-y_rk(n+1);
end
fprintf('打靶法求得y(1)的导数值为:%f\n',z_rk(N+1));
fprintf('打靶法求得y(1)导数值的误差为:%f\n',YU_D-z_rk(N+1));
fprintf('打靶法的迭代次数为:%f\n',nn);
subplot(2,1,1),plot(x,y(x),'b',xx,y_rk,'r')%精确解
title('打靶法结果与精确解比较')
legend('精确解','打靶法','Location','best')
subplot(2,1,2),plot(xx,error,'r') %打靶法误差曲线
title('打靶法误差曲线')
hold off
function z = myrungekutta(f, xx, z0)
n = length(xx);
h = xx(2)-xx(1);
z(1,:)=z0;
for i = 1:n-1
k1 = f(xx(i), z(i,:));
k2=f(xx(i)+0.5*h, z(i,:) + h*k1/2);
k3=f(xx(i)+0.5*h, z(i,:) + h*k2/2);
k4=f(xx(i)+h, z(i,:) + h*k3);
z(i+1,:)=z(i,:) + (k1 + 2*k2 + 2*k3 + k4)*h/6;
end
def shooting(ode, x0, y0, h, xp, yp, eps):
s1 = 0.1
s2 = 0.2
y00 = np.array([0.0, y0])
while true:
y00[0] = s1
x1, y1 = rk4(ode, x0, y00, h, xp)
y00[0] = s2
x2, y2 = rk4(ode, x0, y00, h, xp)
y11 = y1[:, 0]
y22 = y2[:, 0]
if (np.abs(y1[-1, 1] - yp) < eps):
return x1, y11
if (np.abs(y2[-1, 1] - yp) < eps):
return x2, y22
s3 = s2 - (s2 - s1) / (y2[-1, 1] - y1[-1, 1]) * (y2[-1, 1] - yp)
y00[0] = s3
x3, y3 = rk4(ode, x0, y00, h, xp)
y33 = y3[:, 0]
if (np.abs(y3[-1, 1] - yp) < eps):
return x3, y33
s1 = s2
s2 = s3
def ode(x, y):
res = y.copy()
res[0] = y[1]
res[1] = -x * y[1] + 4 * y[0] + 12 * x**2 - 3 * x
return res
x0 = 0
xp = 1.0
y0 = 1.0
yp = 5.0
h = 0.02
eps = 1e-6
xx, yy = shooting(ode, x0, y0, h, xp, yp, eps)
plt.plot(xx, yy)
plt.xlabel('x')
plt.ylabel('y')
plt.show()
考虑一根密度均匀绷紧弦的振动,
\[ \partial_t^2 u = c^2\nabla^2 u \longrightarrow \left\lbrace \begin{aligned}[l] &\partial_t^2 u(x, t) - c^2 \partial_x^2 u(x, t) = 0 \\ &u(0, t) = 0; ~~ u(1, t) = 0; \end{aligned} \right. \]
\[ \begin{aligned}[l] &u(x, t) = T(t)\phi(x) \\ &\frac{T''(t)}{c^2T(t)} = \frac{\phi''(x)}{\phi(x)} \equiv = -k^2 \end{aligned} \longrightarrow \left\lbrace \begin{aligned}[l] &T''(t) + c^2k^2T(t) = 0 \\ &\partial_x^2 \phi = -k^2\phi \end{aligned} \right. \]
分离变量后,空间部分满足的方程和边界条件可以写成 \[ \left\lbrace \begin{aligned}[l] &\partial_x^2 \phi = -k^2\phi; \\ &\phi(0) = \phi(1) = 0 \end{aligned} \right. \] \(k\) wavenumber, \(\phi\) transverse motion of the string.
Explicit solution: \[
\begin{aligned}[l]
&k_n = n\pi; \\
&\phi_n \sim \sin n\pi x;~~ (n = 1, 2, ...)
\end{aligned}
\] 相比边值问题,本征值问题多了一个待定参数 ——
策略
先猜测一个试验本征值 \(k\),同时任取一个非零参数\(\delta\),把微分方程变化为一个初始值问题;
从 \(x = 0\) 向前递推产生一个数值解。如果该数值解在 \(x = 1\)处的值与边界条件 \(\phi(1) = 0\) 在误差范围内不相等,就改变
与打靶法求解边值问题类似,求本征值\(k\) 的过程将成为一个方程求根过程。
此时
因为方程有
二分法的算法实现
给定有根区间 [a, b] ( f(a) f(b) < 0)
和 精度 \(\varepsilon\)
x = (a+b)/2
b – a <
\(\varepsilon\),结束运算,输出 x
f (a) f (x) < 0
, 则令 b = x
,否则令 a = x
, 返回第i步Example 4 用打靶法求解上述紧绷弦问题的最小本征值。
\[ \left\lbrace \begin{aligned}[l] &\frac{d^2\phi}{dx^2} = -k^2\phi, \\ &\phi(0) = \phi(1) = 0, \end{aligned} \right. \longrightarrow \left\lbrace \begin{aligned}[l] &\frac{d^2\phi}{dx^2} = -k^2\phi, \\ &\phi(0) = 0, \phi'(0) = \delta, \end{aligned} \right. \qquad(7)\]
Solution 2. 降阶: \[ \left\lbrace \begin{aligned}[l] &\frac{d\phi}{dx} = z, \\ &\frac{dz}{dx} = -k^2\phi, \\ &\phi(0) = 0, \\ &z(0) = \delta, \end{aligned} \right. \qquad(8)\]
%filename: chapter3_example_4_shooting_method_eigenvalue.m
%用打靶法求解常振动方程的本征值问题,其中用到了二分法和四阶R-K算法
%function shooting_method_RK
clear
clc;close all
format long
DL=0.5*10^-8; %误差判断标准
XL=0.0; %左边界点
XU=1.0; %右边界点
N=100;
h=(XU-XL)/N;
xx=XL:h:XL+N*h;
YL= 0.0; %边界点函数值
YU= 0.0; %边界点函数值
K1= 0.5; %本征值猜测值,第一个
K2= 5; %本征值猜测值,第二个,尝试5、10、20
delta=1.0; %delta 为辅助参数
%---------第一个试探解,求右边界的函数值及误差函数-------------------
f1=@(xx,yy)[yy(2),-K1^2*yy(1)]; % 定义 f1,
z10 = [YL,delta];
z1 = myrungekutta(f1, xx, z10);
y_rk1 = z1(:,1); %用四阶RK算法求得的y值
z_rk1 = z1(:,2);
F1=y_rk1(end)-YU; %用四阶RK算法得到的XU点的函数值与边界条件的差值
%----------第二个试探解,求右边界的函数值及误差函数--------------------
f2=@(xx,yy)[yy(2),-K2^2*yy(1)]; % 定义 f1,
z20 = [YL,delta];
z2 = myrungekutta(f2, xx, z20);
y_rk2 = z2(:,1); %用四阶RK算法求得的y值
%z_rk2 = z2(:,2);
F2=y_rk2(end)-YU; %用四阶RK算法得到的XU点的函数值与边界条件的差值
D=min(abs(F1),abs(F2));
nn=0;
while(D>DL)
nn=nn+1;
%----------二分法求误差方程的根-----------------
if F1*F2>0.0
fprintf('本征值不在两个输入点之间,程序终止');
break;
end
K3=(K1+K2)/2.0;
f3=@(xx,yy)[yy(2),-K3^2*yy(1)]; % 定义 f1,
z30 = [YL,delta];
z3 = myrungekutta(f3, xx, z30);
y_rk3 = z3(:,1); %用四阶RK算法求得的y值
% z_rk3 = z3(:,2);
F3=y_rk3(end)-YU; %用四阶RK算法得到的XU点的函数值与边界条件的差值
if F1*F3>0
K1=K3;
F1=F3;
y_rk1=y_rk3;
elseif F2*F3>0
K2=K3;
F2=F3;
y_rk2=y_rk3;
end
D=min(abs(F1),abs(F2));
plot(xx,y_rk3,'r', LineWidth=3)
ylim([-0.2 0.5]);
title('最小本征值对应的波函数', FontSize=20)
M(nn)=getframe;
end
movie(M,1,2)
if abs(F1)>abs(F2)
y_rk3=y_rk2;
else
y_rk3=y_rk1;
end
hold all
plot(xx,y_rk3,'r', LineWidth=3)
title('最小本征值对应的波函数', 'FontSize', 20)
fprintf('打靶法的迭代次数为:%f\n',nn);
fprintf('打靶法求得的本征值K为:%f\n',K3/pi,K3);
function z = myrungekutta(f, xx, z0)
n = length(xx);
h = xx(2)-xx(1);
z(1,:)=z0;
for i = 1:n-1
k1 = f(xx(i), z(i,:));
k2=f(xx(i)+0.5*h, z(i,:) + h*k1/2);
k3=f(xx(i)+0.5*h, z(i,:) + h*k2/2);
k4=f(xx(i)+h, z(i,:) + h*k3);
z(i+1,:)=z(i,:) + (k1 + 2*k2 + 2*k3 + k4)*h/6;
end
end
eps = 1e-4
x0 = 0.0
xp = 1.0
h = 0.01
y0 = 0.0
yp = 0.0
def ode(x, y, k):
res = y.copy()
res[0] = y[1]
res[1] = -k*k*y[0]
return res
def shooting_evp(ode, x0, y0, h, xp, yp, eps):
k1 = 0.5
k2 = 5.0
delta = 30.0
y00 = np.array([y0, delta])
node = lambda x, y: ode(x, y, k1)
x1, y1 = rk4(node, x0, y00, h, xp)
f1 = y1[-1, 0] - yp
node = lambda x, y: ode(x, y, k2)
x2, y2 = rk4(node, x0, y00, h, xp)
f2 = y2[-1, 0] - yp
if (f1 * f2 > 0):
print("no roots in the interval")
exit(1)
if (f1 * f2 < 0):
if (np.abs(k2 - k1) < eps):
return 0.5 * (k1 + k2), xx, yy
while True:
kk = 0.5 * (k1 + k2)
node = lambda x, y: ode(x, y, kk)
xx, yy = rk4(node, x0, y00, h, xp)
plt.plot(xx, yy[:, 0], '-.')
if (np.abs(k2 - k1) < eps):
return kk, xx, yy
fx = yy[-1, 0] - yp
if (f1 * fx < 0):
k2 = kk
else:
k1 = kk
node = lambda x, y: ode(x, y, k1)
x1, y1 = rk4(node, x0, y00, h, xp)
f1 = y1[-1, 0] - yp
kval, xval, yval = shooting_evp(ode, x0, y0, h, xp, yp, eps)
plt.grid('on')
plt.plot(xval, yval[:, 0], 'k-')
plt.show()
❓ 🤔 二分法求本征值只能求解单根!能否让程序求多根?
简单搜索法的算法实现
给定小于根的起始点\(a\),步长\(h\)和 精度 \(\varepsilon\)
b = a+h
;max(f(a),f(b)) <
\(\varepsilon\),结束运算,输出 b
f (a) f (b) > 0
, 则令 a =b
,否则令 h = h/2
, 返回第1步Example 4 用打靶法求解上述紧绷弦问题的前5个本征值, see (Equation 7), 降阶:(Equation 8).
%filename: chapter3_example_4_shooting_method_eigenvalue_2.m
%用打靶法求解振动方程的本征值问题,可以求多个本征值,
%其中用到了简单搜索法和四阶R-K算法;
function shooting_method_RK_secant
clear all
%close all
clc
format long
DL=0.5*10^-10; %误差判断标准
XL=0.0; %左边界点
XU=1.0; %右边界点
N=1000;
h=(XU-XL)/N;
xx=XL:h:XL+N*h;
YL= 0.0; %边界点函数值
YU= 0.0; %边界点函数值
K1= 0.0; %本征值猜测值,第一个
dk=0.1; %简单搜索法的初始步长
delta=100; %delta为辅助参数,减小
mm=1; %要求的本征值的数目
for ii=1:mm
nn=0;
%---------第一个试探解,求右边界的函数值及误差函数-------------------
f1=@(xx,yy)[yy(2),-K1^2*yy(1)]; % 定义 f1,
z10 = [YL,delta];
z1 = myrungekutta(f1, xx, z10);
y_rk1 = z1(:,1); %用四阶RK算法求得的y值
F1=y_rk1(end)-YU; %用四阶RK算法得到的XU点的函数值与边界条件的差值
D=abs(F1);
while(D>DL)
nn=nn+1;
%----------第二个试探解,求右边界的函数值及误差函数--------------------
K2=K1+dk; %利用简单搜索法向前一步
f2=@(xx,yy)[yy(2),-K2^2*yy(1)]; % 定义 f1,
z20 = [YL,delta];
z2 = myrungekutta(f2, xx, z20);
y_rk2 = z2(:,1); %用四阶RK算法求得的y值
F2=y_rk2(end)-YU; %用四阶RK算法得到的XU点的函数值与边界条件的差值
%----------简单搜索法求误差方程的根-----------------
if F1*F2>0.0
K1=K2;
D=min(abs(F1),abs(F2));
F1=F2;
else
K2=K2-dk;
dk=dk/2;
%D=min(abs(F1),abs(F2));
end
end
hold on
plot(xx,y_rk2)
dk=0.1; %dk恢复初值,搜索下一个本征值;
K1=K2+dk;
fprintf('打靶法的迭代次数为:%f\n',nn);
fprintf('打靶法求得的本征值K为:%f\n',K2/pi);
end
function z = myrungekutta(f, xx, z0)
n = length(xx);
h = xx(2)-xx(1);
z(1,:)=z0;
for i = 1:n-1
k1 = f(xx(i), z(i,:));
k2=f(xx(i)+0.5*h, z(i,:) + h*k1/2);
k3=f(xx(i)+0.5*h, z(i,:) + h*k2/2);
k4=f(xx(i)+h, z(i,:) + h*k3);
z(i+1,:)=z(i,:) + (k1 + 2*k2 + 2*k3 + k4)*h/6;
end
%filename: chapter3_example_4_shooting_method_eigenvalue_3.m
%用打靶法求解振动方程的本征值问题,可以求多个本征值,
%其中用到了简单搜索法(结合二分法)和四阶R-K算法.
function shooting_method_RK_bisection
clear all
close all
clc
format long
DL=0.5*10^-10; %误差判断标准
XL=0.0; %左边界点
XU=1.0; %右边界点
N=1000;
h=(XU-XL)/N;
xx=XL:h:XL+N*h;
YL= 0.0; %边界点函数值
YU= 0.0; %边界点函数值
K1= 0.0; %本征值猜测值,第一个
dk=0.1; %简单搜索法的初始步长
delta=300; %delta为辅助参数
mm=5; %要求的本征值的数目
%hold on
for ii=1:mm
nn=0;
%---------第一个试探解,求右边界的函数值及误差函数-------------------
f1=@(xx,yy)[yy(2),-K1^2*yy(1)]; % 定义 f1,
z10 = [YL,delta];
z1 = myrungekutta(f1, xx, z10);
y_rk1 = z1(:,1); %用四阶RK算法求得的y值
F1= y_rk1(end)-YU; %用四阶RK算法得到的XU点的函数值与边界条件的差值
D=abs(F1);
while(D>DL)
nn=nn+1;
%----------第二个试探解,求右边界的函数值及误差函数--------------------
K2=K1+dk; %利用简单搜索法向前一步
f2=@(xx,yy)[yy(2),-K2^2*yy(1)]; % 定义 f2,
z20 = [YL,delta];
z2 = myrungekutta(f2, xx, z20);
y_rk2 = z2(:,1); %用四阶RK算法求得的y值
F2 = y_rk2(end)-YU; %用四阶RK算法得到的XU点的函数值与边界条件的差值
%----------简单搜索法结合二分法求误差方程的根-----------------
if F1*F2>0.0
K1=K2;
D=min(abs(F1),abs(F2));
F1=F2;
else
D=min(abs(F1),abs(F2));
while(D>DL) %进入二分法循环
nn=nn+1;
K3=(K1+K2)/2;
f3=@(xx,yy)[yy(2),-K3^2*yy(1)]; % 定义 f3,
z30 = [YL,delta];
z3 = myrungekutta(f3, xx, z30);
y_rk3 = z3(:,1); %用四阶RK算法求得的y值
F3=y_rk3(end)-YU; %用四阶RK算法得到的XU点的函数值与边界条件的差值
if F3*F2>0.0
K2=K3;
F2=F3;
else
K1=K3;
F1=F3;
end
D=min(abs(F1),abs(F2));
end %结束二分法循环
end
end
dk=0.1;
K1=K2+dk;
figure;plot(xx,y_rk3,'r')
fprintf('打靶法的迭代次数为:%f\n',nn);
fprintf('打靶法求得的本征值K为:%f\n',K2/pi);
end
%hold off
function z = myrungekutta(f, xx, z0)
n = length(xx);
h = xx(2)-xx(1);
z(1,:)=z0;
for i = 1:n-1
k1 = f(xx(i), z(i,:));
k2=f(xx(i)+0.5*h, z(i,:) + h*k1/2);
k3=f(xx(i)+0.5*h, z(i,:) + h*k2/2);
k4=f(xx(i)+h, z(i,:) + h*k3);
z(i+1,:)=z(i,:) + (k1 + 2*k2 + 2*k3 + k4)*h/6;
end
eps = 1e-4
x0 = 0.0
xp = 1.0
h = 0.1
y0 = 0.0
yp = 0.0
def ode(x, y, k):
res = y.copy()
res[0] = y[1]
res[1] = -k*k*y[0]
return res
def shooting_evp(ode, x0, y0, h, xp, yp, eps, n):
j = 1
k1 = 0.0
k2 = k1 + h
delta = 30.0
y00 = np.array([y0, delta])
node = lambda x, y: ode(x, y, k1)
x1, y1 = rk4(node, x0, y00, h, xp)
f1 = y1[-1, 0] - yp
node = lambda x, y: ode(x, y, k2)
x2, y2 = rk4(node, x0, y00, h, xp)
f2 = y2[-1, 0] - yp
if (f1 * f2 > 0):
k1 = k2
k2 = k1 + h
if (f1 * f2 < 0):
if (np.abs(k2 - k1) < eps):
print(j, kk)
plt.plot(xx, yy[:, 0], label = 'k = ' + str(j))
j = j + 1
while j < n:
kk = 0.5 * (k1 + k2)
node = lambda x, y: ode(x, y, kk)
xx, yy = rk4(node, x0, y00, h, xp)
plt.plot(xx, yy[:, 0], '-.')
if (np.abs(k2 - k1) < eps):
print(j, kk)
plt.plot(xx, yy[:, 0], label = 'k = ' + str(j))
j = j + 1
k1 = k2
k2 = k1 + h
fx = yy[-1, 0] - yp
if (f1 * fx < 0):
k2 = kk
else:
k1 = kk
node = lambda x, y: ode(x, y, k1)
x1, y1 = rk4(node, x0, y00, h, xp)
f1 = y1[-1, 0] - yp
shooting_evp(ode, x0, y0, h, xp, yp, eps, 3)
plt.grid('on')
plt.legend()
plt.show()
1 0.199951171875
2 0.29995114803314216
An important class of linear equations in physics is known as Sturm-Liouville problem, defined by \[
[p(x)u'(x)]' + q(x)u(x) = s(x)
\] which has the
Here, \(p(x), q(x)\) and \(s(x)\) are the coefficient functions of \(x\).
For most actual problems, \(s(x) = 0\) and \(q(x) = -r(x) + \lambda w(x)\), where \(\lambda\) is the eigenvalue of the equation, and \(r(x)\) and \(w(x)\) are the redefined coefficient functions.
The Legendre equation
, the Bessel equation
, and their related equation in physics are the examples of the Sturm-Liouville problem.
with \[ \Delta_1 = \frac{u_{i+1} - u_{i-1}}{2h} = u'_i + \frac{h^2 u_i^{(3)}}{3!} + O(h^4) \qquad(9)\]
and
\[ \Delta_2 = \frac{u_{i+1} -2u_i + u_{i-1}}{h^2} = u''_i + \frac{h^2u^{(4)}_i}{4!} + O(h^4) \qquad(10)\]
Multiply (Equation 9) by \(p'_i\) and (Equation 10) by \(p_i\) and add them together, we have
\[ p'_i \Delta_1 + p_i \Delta_2 = \underbrace{(p_iu'_i)'}_{s_i - q_i u_i} + \underbrace{\frac{h^2}{12} \left(p_iu^{(4)}_i + 2p'_i u^{(3)}_i\right) + O(h^4)}_\text{remove all} \] Simplest numerical algorithm:
\[ \rightarrow (2p_i + hp'_i)u_{i+1} + (2p_i - hp'_i)u_{i-1} = 4p_iu_i + 2h^2(s_i - q_iu_i) \]
Legendre equation
\[ \frac{d}{dx}\left[(1-x^2) \frac{du}{dx}\right] + l(l+1)u = 0 \] with \(l = 0, 1, \dots ,\infty\) and \(x \in [-1, 1]\). The solutions of the Legendre equation are the Legendre polynomials \(P_l(x)\).
\[ p(x) = 1-x^2; ~~~~ q(x) = l(l+1); ~~~~ s(x) = 0; \]
\[ \begin{eqnarray} \Rightarrow & &\left[2(1-x_i^2) + h (-2x_i)\right]u_{i+1} + \left[2(1-x_i^2) - h(-2x_i)\right]u_{i-1} \\ &=& 4\left[(1-x_i^2) - 2h^2l(l+1)\right]u_i \end{eqnarray} \]
\[ \Rightarrow u_{i+1} = \alpha_i \left[\beta_i u_{i} + \gamma_{i}u_{i-1}\right] \] with \[\alpha_i \equiv \frac{1}{2p_i + hp'_i} = \frac{1}{2(1-x_i^2) + h(-2x_i)},\] \[\beta_i \equiv 4p_i - 2h^2 q_i = 4\left[(1-x_i^2) - 2h^2l(l+1)\right],\] \[\gamma_i \equiv 2p_i - hp'_i = \left[2(1-x_i^2) - h(-2x_i)\right].\]
Assume we don’t know the value of \(l\) but the first two points of \(P_l(x) = x\); then we can treat the problem as an eigenvalue problem.
import numpy as np
import matplotlib.pyplot as plt
def simple_solve(x, y0, p, p1, l, s):
res = np.zeros(x.shape)
res[0] = y0[0]
res[1] = y0[1]
h = x[1] - x[0]
q = l * (l + 1.0)
alpha = 1.0 / (2.0 * p + h * p1)
beta = 4.0 * p - 2.0 * h**2 * q
gamma = h * p1 - 2.0 * p
s0 = 2*h**2 * s
for i in range(1, len(x) - 1):
res[i+1] = alpha[i] * (beta[i] * res[i] + gamma[i] * res[i-1] + s0[i])
return res
def ieqn(l):
nn = 101
x = np.linspace(0.0, 1.0, nn)
h = x[1] - x[0]
p = (1.0 - x**2.0)
p1 = -2.0 * x
s = np.zeros(x.shape)
y0 = np.array([0.0, h])
res = simple_solve(x, y0, p, p1, l, s)
return res[-1] - 1.0
def root_find(l0, dl, ln, leps):
inn = 0
l00 = l0
l01 = l0 + dl
result = np.zeros(ln)
while inn < ln:
f0 = ieqn(l00)
f1 = ieqn(l01)
#print("%6.5f\t%6.5f\t%6.5f\t%6.5f" %(l00, l01, f0, f1))
if (f0 * f1 > 0):
l00 = l00 + dl
l01 = l00 + dl
else:
if (np.abs(l01 - l00) < leps):
result[inn] = 0.5 * (l00 + l01)
inn += 1
l00 = l01
l01 = l01 + dl
else:
temp = 0.5 * (l00 + l01)
f1 = ieqn(temp)
if (f0 * f1 > 0):
l00 = temp
else:
l01 = temp
return result
l0 = 0.5
dl = 0.1
ln = 3
leps = 1e-10
result = root_find(l0, dl, ln, leps)
print(result)
[ 1. 36.48417442 37.60636423]
一维薛定谔方程的一般形式
\[ i\hbar \frac{\partial}{\partial t} \psi(x,t) = [-\frac{\hbar^2}{2m} \frac{\partial^2}{\partial x^2} + V(x)]\psi(x,t) \]
粒子在稳定势场 \(V(x)\) 中运动,\(V(x)\) 与总能量 \(E\) 不随时间变化,即为定态。此时波函数形式为:
\[ \psi(x,t) = \psi(x)e^{-iEt/\hbar} \]
带入上述一维薛定谔方程的一般形式可得:
\[ \frac{d^2}{dx^2}\psi(x) + \frac{2m}{\hbar^2}[E - V(x)]\psi(x) = 0 \]
Problem: \[ \frac{d^2\psi(x)}{dx^2} + \frac{2m}{\hbar^2}[E - V(x)]\psi(x) = 0 \]
let \[ k^2(x) = \frac{2m}{\hbar^2}[E - V(x)] \] . . .
\[ \psi(x_{min}) = \psi(x_{max}) = 0 \]
\[ \frac{d^2\psi(x)}{dx^2} + k^2(x)\psi(x) = 0 \]
求使该问题有非零解的能量本政值\(E\)以及波函数。
%filename: chapter3_schrodinger.m
%用两端积分方法求解schrodinger方程的本征值与本征函数,程序正确
%利用两点差分公式求得,左边部分为向后差分公式,右边部分为向前差分公式。
function chapter4_schrodinger
clear
clc;close all
xmin=-5;xmax=5; %势函数的边界点
n=1000;
gamma=sqrt(2);
Tol=10^(-15); %误差标准
e=-1; %此处能量本征值之所以从-1开始,是因为势能函数v的最小值为-1
m=5;
psi=zeros(2*n+1,m);
x_r=zeros(2*n+1,m);
for j=1:m
%本征值求很多时,本征函数输出存在问题,后面的冲掉了前面的结果,找出问题所在
%目前的程序已经fix了这个问题,该问题出自不同本征函数对应的离散化自变量数组不同
de=0.01;%恢复de的值,用于求下一个本征值
e=e+de;
xm=matchpoint(xmin,e,Tol) %求能量e与势能相等的交叉点
[x,y]=equel(xm,n,e,xmin,xmax,gamma); %求拼接后的本征函数数组
while abs(de)>Tol
e=e+de; xm=matchpoint(xmin,e,Tol);
[xz,yz]=equel(xm,n,e,xmin,xmax,gamma);
a=(y(n+1)-y(n))/(x(n+1)-x(n))-(y(n+1)-y(n+2))/(x(n+1)-x(n+2));
%a为从不同方向计算所得的一阶导数值的差,向前向后差分
b=(yz(n+1)-yz(n))/(xz(n+1)-xz(n))-(yz(n+1)-yz(n+2))/(xz(n+1)-xz(n+2));
if a*b>0
else
e=e-de;
de=de/2;%简单搜索法,步长减半
end
end
% The normalization
c=0;
for i=1:2*n
c=c+(x(i+1)-x(i))/2*(yz(i)^2+yz(i+1)^2);
%利用梯形法做积分,从xmin积分到xmax,从而做归一化
end
psi(:,j)=yz./sqrt(c);%归一化后的波函数
E(j)=e;
x_r(:,j)=xz;
end
E'
for i=1:m
figure;plot(x_r(:,i),psi(:,i),'r')
title('归一化后的波函数')
%figure;plot(x_r(:,i),psi(:,i).*psi(:,i),'ro')
%title('几率密度分布')
end
pv=-(1-1/2*x.^2);
figure;plot(x,pv,'b')
title('势能函数')
function x = matchpoint(xmin, e, Tol) %求xm点,即E与V相同时的x值
x=xmin;
dx=0.01;
fo=v1(x);
while abs(dx)>Tol
x=x+dx;
if (fo-e)*(v1(x)-e)>0
else
x=x-dx; %简单搜索法,求出的是左边的xm值
dx=dx/2;
end
end
function [x,y]=left(delta,xm,n,e,xmin,gamma) %求得从左边积分时xm处的波函数值
h=(xm-xmin)/n;
y(1)=0;
y(2)=delta; %delta为辅助参量,对结果没有影响
x=xmin:h:xm;
for i=2:n
%Numerov算法
y(i+1)=2*(1-5/12*h^2*gamma^2*(e-v1(x(i))))*y(i)-(1+h^2/12*gamma^2*(e-v1(x(i-1))))*y(i-1);
y(i+1)=y(i+1)/(1+h^2/12*gamma^2*(e-v1(x(i+1))));
end
function [x,y]=right(delta,xm,n,e,xmax,gamma)%求得从右边积分时xm处的波函数值
h=(xmax-xm)/n;
y(n+1)=0;
y(n)=delta;
x=xm:h:xmax;
for i=n:-1:2
%Numerov算法
y(i-1)=2*(1-5/12*h^2*gamma^2*(e-v1(x(i))))*y(i)-(1+h^2/12*gamma^2*(e-v1(x(i+1))))*y(i+1);
y(i-1)=y(i-1)/(1+h^2/12*gamma^2*(e-v1(x(i-1))));
end
function [x,y]=equel(xm,n,e,xmin,xmax,gamma) %拼接
delta=0.4;
[x1,y1]=left(delta,xm,n,e,xmin,gamma);
[x2,y2]=right(delta,xm,n,e,xmax,gamma);
y1=y1.*y2(1)/y1(n+1); %此处y2(1)与y1(n+1)均为xm处的波函数值,此时y1(n+1)将变成y2(1)
x=[x1';x2(2:end)']; %将两段进行拼接,成为整个积分区间,从xmin到xmax,不存在重复点
y=[y1';y2(2:end)'];
function v=v1(x)
v=-(1-1/2*x^2);
%filename: chapter3_schrodinger_equation_1.m
%用打靶法求解常微分方程的边值问题,其中用到了弦割法和四阶R-K算法
%改程序对应于chapter4_schrodinger_shooting_method.m,解k2确定下的边值问题
function chapter4_schrodinger_equation_1
clear
clc,close all
format long
DL=0.5*10^-6; %误差判断标准,用于弦割法
XL=-5.0; %左边界点
XU=5.0; %右边界点
h=0.02;
N=(XU-XL)/h;
xx=XL:h:XL+N*h;
D = 1; %初始化误差
YL= 0.0; %边界点函数值
YU= 0.0; %边界点函数值
X0= 3.5; %起始点-终点对应的斜率,作为XL处斜率的猜测值,弦割法第一个启动点
X1= 4.5; %第二个斜率猜测值,作为弦割法的第二个启动点
y_rk1=zeros(1,N+1);
y_rk2=zeros(1,N+1);
y_rk=zeros(1,N+1);
nn=0;
while(D>DL)
%-----------四阶R-K算法-------------------
nn=nn+1;
%---------第一个试探解,求右边界的函数值及误差函数-------------------
f=@(xx,yy)[yy(2),-2*yy(1)]; %转换成解边值问题,k2给定,为-2,解具有指数行为
% 定义 f1,yy(1)系数为正,对应于gamma^2*(V(xx)-e1)=-2,e1>V,解振荡,系数为负解具有指数行为
z10 = [YL,X0];
z1 = myrungekutta(f, xx, z10);
y_rk1 = z1(:,1); %用四阶RK算法求得的y值
F0=z1(length(xx))-YU %用四阶RK算法得到的XU点的函数值与边界条件的差值
%----------第二个试探解,求右边界的函数值及误差函数--------------------
z20 = [YL,X1];
z2 = myrungekutta(f, xx, z20);
y_rk2 = z2(:,1); %用四阶RK算法求得的y值
F1=z2(length(xx))-YU %用四阶RK算法得到的XU点的函数值与边界条件的差值
%----------弦割法求误差方程的根-----------------
D=min(abs(F1),abs(F0));
X2=X1-F1*(X1-X0)/(F1-F0);
X0=X1;
X1=X2;
end
if (abs(F1)>abs(F0))
y_rk=y_rk1;
else
y_rk=y_rk2;
end
plot(xx,y_rk,'r')
title('解的行为与经典禁区内外的关系图')
function z = myrungekutta(f, xx, z0)
n = length(xx);
h = xx(2)-xx(1);
z(1,:)=z0;
for i = 1:n-1
k1 = f(xx(i), z(i,:));
k2=f(xx(i)+0.5*h, z(i,:) + h*k1/2);
k3=f(xx(i)+0.5*h, z(i,:) + h*k2/2);
k4=f(xx(i)+h, z(i,:) + h*k3);
z(i+1,:)=z(i,:) + (k1 + 2*k2 + 2*k3 + k4)*h/6;
end
%filename: chapter3_schrodinger_2.m
%用两端积分方法求解schrodinger方程的本征值与本征函数,程序正确
%利用三点差分公式求xm处的导数连续
%function chapter4_schrodinger
clear
clc;close all
xmin=0.7;xmax=4; %势函数的边界点
n=1000;
gamma=sqrt(2);
gamma=21.7;
Tol=10^(-16); %误差标准
e=-1; %此处能量本征值之所以从-1开始,是因为势能函数v的最小值为-1
m=4;
psi=zeros(2*n+1,m);
x_r=zeros(2*n+1,m);
for j=1:m
%本征值求很多时,本征函数输出存在问题,后面的冲掉了前面的结果,找出问题所在
%目前的程序已经fix了这个问题,该问题出自不同本征函数对于的离散化自变量数组不同
de=0.01;%恢复de的值,用于求下一个本征值
e=e+de;
xm=matchpoint(xmin,e,Tol) %求能量e与势能相等的交叉点
[x,y,xx,yy]=equel(xm,n,e,xmin,xmax,gamma); %求拼接后的本征函数数组
while abs(de)>Tol
e=e+de;
xm=matchpoint(xmin,e,Tol);
[xz,yz,xxz,yyz]=equel(xm,n,e,xmin,xmax,gamma);
a=(yy(n+2)-yy(n))/(xx(n+2)-xx(n))-(yy(n+5)-yy(n+3))/(xx(n+5)-xx(n+3));
%y(n+1)与y(n+2)均为xm处的波函数值,a为从不同方向
%计算所得的一阶导数值的差,对应于方程(3.22)
b=(yyz(n+2)-yyz(n))/(xxz(n+2)-xxz(n))-(yyz(n+5)-yyz(n+3))/(xxz(n+5)-xxz(n+3));
if a*b>0
else
e=e-de;
de=de/2;%简单搜索法,步长减半
end
end
% The normalization
c=0;
for i=1:2*n
c=c+(xz(i+1)-xz(i))/2*(yz(i)^2+yz(i+1)^2);
%利用梯形法做积分,从xmin积分到xmax,从而做归一化
end
psi(:,j)=yz./sqrt(c);%归一化后的波函数
E(j)=e;
x_r(:,j)=xz;
end
for i=1:m
figure;plot(x_r(:,i),psi(:,i),'ro')
title('归一化后的波函数')
figure;plot(x_r(:,i),psi(:,i).*psi(:,i),'ro')
title('几率密度分布')
end
pv=-(1-1/2*x.^2);
figure;%plot(x,pv,'b')
v0=1.0;
v0=4.747;
E' *v0
hold on;
for k=1:m
plot([1.0, 2], [1, 1]*E(k) * v0); %画振动能级
end
ve = @(x) 4.0 * (x.^(-12.0) - x.^(-6.0))*v0;
fplot(@(x) ve(x), [1.0 2]);
title('势能函数')
function x = matchpoint(xmin, e, Tol) %求xm点,即E与V相同时的x值
x=xmin;
dx=0.01;
while abs(dx)>Tol
fo=v1(x);
x=x+dx;
if (fo-e)*(v1(x)-e)>0
else
x=x-dx; %简单搜索法,求出的是左边的xm值
dx=dx/2;
end
end
end
function [x,y]=left(delta,xm,n,e,xmin,gamma)
%求得从左边积分时xm处的波函数值,以及xm+h处的波函数值
h=(xm-xmin)/n;
y(1)=0;
y(2)=delta; %delta为辅助参量,对结果没有影响
x=xmin:h:xm+h;
for i=2:n+1
%Numerov算法
y(i+1)=2*(1-5/12*h^2*gamma^2*(e-v1(x(i))))*y(i)-(1+h^2/12*gamma^2*(e-v1(x(i-1))))*y(i-1);
y(i+1)=y(i+1)/(1+h^2/12*gamma^2*(e-v1(x(i+1))));
end
end
function [x,y]=right(delta,xm,n,e,xmax,gamma)
%求得从右边积分时xm处的波函数值,以及xm-h处的波函数值
h=(xmax-xm)/n;
y(n+2)=0;
y(n+1)=delta;
x=xm-h:h:xmax;
for i=n+1:-1:2
%Numerov算法
y(i-1)=2*(1-5/12*h^2*gamma^2*(e-v1(x(i))))*y(i)-(1+h^2/12*gamma^2*(e-v1(x(i+1))))*y(i+1);
y(i-1)=y(i-1)/(1+h^2/12*gamma^2*(e-v1(x(i-1))));
end
end
function [x,y,xx,yy]=equel(xm,n,e,xmin,xmax,gamma) %拼接
delta=0.4;
[x1,y1]=left(delta,xm,n,e,xmin,gamma);
[x2,y2]=right(delta,xm,n,e,xmax,gamma);
y1=y1.*y2(2)/y1(n+1); %此处y2(2)与y1(n+1)均为xm处的波函数值,此时y1(n+1)将变成y2(2)
x=[x1(1:n+1)';x2(3:n+2)']; %将两段进行拼接,成为整个积分区间,从xmin到xmax
y=[y1(1:n+1)';y2(3:n+2)']; %拼接后,不存在重复点
xx=[x1(1:n+2)';x2(1:n+2)'];
yy=[y1(1:n+2)';y2(1:n+2)'];
end
function v=v1(x)
v = 4.0 * (x.^(-12.0) - x.^(-6.0));
end
%filename: chapter3_schrodinger_3.m
%用两端积分方法求解schrodinger方程的本征值与本征函数,程序正确
%利用两点差分公式求得,都是向后差分公式
function chapter4_schrodinger
clc;clear;close all
xmin=-5; %势函数的边界点
xmax=-xmin;
n=1000;
gamma=sqrt(80);
Tol=10^(-15); %误差标准
e=-1; %此处能量本征值之所以从-1开始,是因为势能函数v的最小值为-1
m=5; %待求本征值的个数
psi=zeros(2*n+1,m);
x_r=zeros(2*n+1,m);
for j=1:m
%本征值求很多时,本征函数输出存在问题,后面的冲掉了前面的结果,找出问题所在
%目前的程序已经fix了这个问题,该问题出自不同本征函数对于的离散化自变量数组不同
de=0.01;%恢复de的值,用于求下一个本征值
e=e+de;
xm=matchpoint(xmin,e,Tol) %求能量e与势能相等的交叉点,求得左边的xm值
[x,y,xx,yy]=equel(xm,n,e,xmin,xmax,gamma); %求拼接后的本征函数数组
while abs(de)>Tol
e=e+de; xm=matchpoint(xmin,e,Tol);
[xz,yz,xxz,yyz]=equel(xm,n,e,xmin,xmax,gamma);
a=(yy(n+1)-yy(n))/(xx(n+1)-xx(n))-(yy(n+3)-yy(n+2))/(xx(n+3)-xx(n+2));
%yy(n+1)与yy(n+3)均为xm处的波函数值,a为从不同方向计算所得的一阶导数值的差,对应于方程(3.22)
b=(yyz(n+1)-yyz(n))/(xxz(n+1)-xxz(n))-(yyz(n+3)-yyz(n+2))/(xxz(n+3)-xxz(n+2));
if a*b>0
else
e=e-de;
de=de/2;%简单搜索法,步长减半
end
end
% The normalization
c=0;
for i=1:2*n
c=c+(xz(i+1)-xz(i))/2*(yz(i)^2+yz(i+1)^2);
%利用梯形法做积分,从xmin积分到xmax,从而做归一化
%注意:由于两段积分步长不同,我们并没有使用simpson算法
end
psi(:,j)=yz(:)/sqrt(c);%归一化后的波函数
E(j)=e;
x_r(:,j)=xz;
xmm(j)=xm; %为了显示经典禁区,xmin-xm为经典禁区的一部分
xzz(j)=n+1; %为了给出xm在自变量数列中的位置
end
E'
for i=1:m-1
E(i+1)-E(i) %输出相邻能级间隔
end
for i=1:m
figure;plot(x_r(:,i),psi(:,i),'r',xmm(i),psi(xzz(i),i),'bo')
title('归一化后的波函数')
%我们只画出了经典禁区的左边部分
%figure;plot(x_r(:,i),psi(:,i).*psi(:,i),'ro')
%title('几率密度分布')
end
pv=-(1-1/2*x.^2);
figure;plot(x,pv,'b')
title('势能函数')
function x = matchpoint(xmin, e, Tol)
%求xm点,即E与V相同时的x值
x=xmin;
dx=0.01;
%fo=v1(x);
while abs(dx)>Tol
fo=v1(x);
x=x+dx;
if (fo-e)*(v1(x)-e)>0 %求根函数为v1(x)-e
else
x=x-dx; %简单搜索法,求出的是左边的xm值
dx=dx/2;
end
end
function [x,y]=left(delta,xm,n,e,xmin,gamma) %求得从左边积分时xm处的波函数值
h=(xm-xmin)/n;
y(1)=0;
y(2)=delta; %delta为辅助参量,对结果没有影响
x=xmin:h:xm;
for i=2:n
%Numerov算法
y(i+1)=2*(1-5/12*h^2*gamma^2*(e-v1(x(i))))*y(i)-(1+h^2/12*gamma^2*(e-v1(x(i-1))))*y(i-1);
y(i+1)=y(i+1)/(1+h^2/12*gamma^2*(e-v1(x(i+1))));
end
function [x,y]=right(delta,xm,n,e,xmax,gamma)%求得从右边积分时xm处的波函数值
h=(xmax-xm)/n;
y(n+2)=0;
y(n+1)=delta;
x=xm-h:h:xmax;
for i=n+1:-1:2
%Numerov算法
y(i-1)=2*(1-5/12*h^2*gamma^2*(e-v1(x(i))))*y(i)-(1+h^2/12*gamma^2*(e-v1(x(i+1))))*y(i+1);
y(i-1)=y(i-1)/(1+h^2/12*gamma^2*(e-v1(x(i-1))));
end
function [x,y,xx,yy]=equel(xm,n,e,xmin,xmax,gamma) %拼接
delta=0.4;
[x1,y1]=left(delta,xm,n,e,xmin,gamma);
[x2,y2]=right(delta,xm,n,e,xmax,gamma);
y1=y1.*y2(2)/y1(n+1);
%此处y2(2)与y1(n+1)均为xm处的波函数值,此时y1(n+1)将变成y2(2),使得函数值在xm处相同
x=[x1';x2(3:n+2)'];%将两段进行拼接,成为整个积分区间,从xmin到xmax,不存在重复点
y=[y1';y2(3:n+2)'];
%上下分布采用了两种不同的拼接方式
xx=[x1';x2']; %将两段进行拼接,成为整个积分区间,从xmin到xmax,
%存在一个重复点,xx(n+3)与xx(n+1)重复,xx(n+2)与xx(n)一般不重复
yy=[y1';y2'];
function v=v1(x) %定义势函数
v=-(1-1/2*x^2);
%filename: chapter3_schrodinger_wrong.m
%用两端积分方法求解schrodinger方程的本征值与本征函数,程序存在问题
%本征值求很多时,本征函数输出存在问题,后面的冲掉了前面的结果,找出问题所在
function chapter4_schrodinger
xmin=-5;xmax=5; %势函数的边界点
n=1000;
gamma=sqrt(2);
Tol=10^(-15); %误差标准
e=-1; %此处能量本征值之所以从-1开始,是因为势能函数v的最小值为-1
m=5;
psi=zeros(2*n+2,m);%从xm到xmin或xmax分成n段(n+1个点)
for j=1:m
de=0.05;%恢复de的值,用于求下一个本征值,de=0.04
e=e+de;
xm=matchpoint(xmin,e,Tol) %求能量e与势能相等的交叉点
[x,y]=equel(xm,n,e,xmin,xmax,gamma); %求拼接后的本征函数数组
while abs(de)>Tol
e=e+de; xm=matchpoint(xmin,e,Tol);
[x,yz]=equel(xm,n,e,xmin,xmax,gamma);
a=(y(n+1)-y(n))/(x(n+1)-x(n))-(y(n+3)-y(n+2))/(x(n+3)-x(n+2));
%y(n+1)与y(n+2)均为xm处的波函数值,a为从不同方向
%计算所得的一阶导数值的差,对应于方程(3.22),注意求根函数的定义
b=(yz(n+1)-yz(n))/(x(n+1)-x(n))-(yz(n+3)-yz(n+2))/(x(n+3)-x(n+2));
if a*b>0
else
e=e-de;
de=de/2;%简单搜索法,步长减半
end
end
% The normalization
c=0;
for i=1:2*n+1
c=c+(x(i+1)-x(i))/2*(yz(i)^2+yz(i+1)^2);
%利用梯形法做积分,从xmin积分到xmax,从而做归一化
end
%c=c-(x(n+2)-x(n+1))/2*(yz(n+1)^2+yz(n+2)^2);%此式可以没有,因为x(n+2)-x(n+1)==0
psi(:,j)=yz./sqrt(c);%归一化后的波函数
E(j)=e;
end
E'
figure(1);plot(x,psi(:,1),'ro')
title('归一化后的波函数')
pv=-(1-1/2*x.^2);
figure(2);plot(x,pv,'b')
title('势能函数')
figure(3);plot(x,psi(:,1).*psi(:,1),'ro')
title('几率密度分布')
function x = matchpoint(xmin, e, Tol) %求xm点,即E与V相同时的x值
x=xmin;
dx=0.01;
fo=v1(x);
while abs(dx)>Tol
x=x+dx;
if (fo-e)*(v1(x)-e)>0
else
x=x-dx; %简单搜索法,求出的是左边的xm值
dx=dx/2;
end
end
function [x,y]=left(delta,xm,n,e,xmin,gamma)
%求得从左边积分时xm处的波函数值
h=(xm-xmin)/n;
y(1)=0;
y(2)=delta; %delta为辅助参量,对结果没有影响
x=xmin:h:xm;
for i=2:n
%Numerov算法
y(i+1)=2*(1-5/12*h^2*gamma^2*(e-v1(x(i))))*y(i)-(1+h^2/12*gamma^2*(e-v1(x(i-1))))*y(i-1);
y(i+1)=y(i+1)/(1+h^2/12*gamma^2*(e-v1(x(i+1))));
end
function [x,y]=right(delta,xm,n,e,xmax,gamma)
%求得从右边积分时xm处的波函数值
h=(xmax-xm)/n;
y(n+1)=0;
y(n)=delta;
x=xm:h:xmax;
for i=n:-1:2
%Numerov算法
y(i-1)=2*(1-5/12*h^2*gamma^2*(e-v1(x(i))))*y(i)-(1+h^2/12*gamma^2*(e-v1(x(i+1))))*y(i+1);
y(i-1)=y(i-1)/(1+h^2/12*gamma^2*(e-v1(x(i-1))));
end
function [x,y]=equel(xm,n,e,xmin,xmax,gamma) %拼接
delta=0.4; %辅助函数
[x1,y1]=left(delta,xm,n,e,xmin,gamma);
[x2,y2]=right(delta,xm,n,e,xmax,gamma);
y1=y1.*y2(1)/y1(n+1); %此处y2(1)与y1(n+1)均为xm处的波函数值,此时y1(n+1)将变成y2(1)
x=[x1';x2']; %将两段进行拼接,成为整个积分区间,从xmin到xmax
y=[y1';y2'];
function v=v1(x)
v=-(1-1/2*x^2);
%filename: chapter3_schrodinger_shooting_method.m
%用打靶法求解schrodinger方程的本征值问题,可以求多个本征值,
%其中用到了简单搜索法和四阶R-K算法,本征值问题
% y''(x)=gamma^2*(V(x)-e)*y(x) 其中 V(x)=-1+1/2*x^2
% y(-5)=0;y(5)=0;
%RK法在经典禁戒区出现灾难性指数性增长,
%使得即使简单搜索法中步长de即使取到10^-16
%仍然不能使右端点函数值达到0,程序进入死循环
%对gamma值较小的情况,程序虽然能够运行,但求得本征值有误,不满足(n+1/2)hw关系
function schrodinger_shooting_method
clear
clc
close all
format long
DL=0.5e-9; %误差判断标准,放松的,0.5e-19
gamma=sqrt(20); %schrodinger方程参数,对gamma值较小的情况,程序虽然能够运行,但求得本征值有误,不满足(n+1/2)hw关系
V=@(x)-1+1/2*x^2; %势能
XL=-5.0; %左边界点
XU=5.0; %右边界点
N=1000; %N等分
h=(XU-XL)/N; %步长
xx=XL:h:(XL+N*h); %自变量,RK算法
YL= 0.0; %左边界点函数值
YU= 0.0; %右边界点函数值
e1= 0; %本征值猜测值,第一个
de=0.005; %简单搜索法的初始步长
DYL=1; %DYL为辅助参数
mm=1; %要求的本征值的数目
for ii=1:mm
D = 1; %每次都要初始化误差
nn=0; %迭代次数
while(D>DL)
%-----------四阶R-K算法-------------------
nn=nn+1
%---------第一个试探解,求右边界的函数值及误差函数-------------------
f1=@(xx,yy)[yy(2),gamma^2*(V(xx)-e1)*yy(1)]; % 定义 f1
z10 = [YL,DYL];
z1 = myrungekutta(f1, xx, z10);
y_rk1= z1(:,1); %用四阶RK算法求得的y值
F1=z1(length(xx),1)-YU %用四阶RK算法得到的XU点的函数值与边界条件的差值
%----------第二个试探解,求右边界的函数值及误差函数--------------------
e2=e1+de; %利用简单搜索法向前一步
f2=@(xx,yy)[yy(2),gamma^2*(V(xx)-e2)*yy(1)]; % 定义 f2
z20= [YL,DYL];
z2=myrungekutta(f2, xx, z20);
y_rk2 = z2(:,1); %用四阶RK算法求得的y值
F2=z2(length(xx),1)-YU %用四阶RK算法得到的XU点的函数值与边界条件的差值
%----------简单搜索法求误差方程的根-------------------------------------
if F1*F2>0.0
e1=e2;
D=max(abs(F1),abs(F2));
else
e2=e2-de;
de=de/2;
%D=max(abs(F1),abs(F2));
end
end
%归一化 求积分 梯形法
int=0;
for i=1:length(xx)-1
int=int+(xx(i+1)-xx(i))/2*(y_rk2(i)^2+y_rk2(i+1)^2);%利用梯形法做积分
end
yy(:,ii)=y_rk2(:)./sqrt(int); %归一化后的波函数,共mm列
e(ii)=e2; %能量本征值
n(ii)=nn; %迭代次数
de=0.05; %de恢复初值
e1=e2+de; %搜索下一个本征值
end
%可视化
for jj=1:mm
figure
plot(xx,yy(:,jj),'r')
title(['归一化波函数e(',num2str(jj),')=',num2str(e(jj))])
fprintf('打靶法求得的第%d本征值e为:%f\n',jj,e(jj));
fprintf('打靶法的迭代次数为:%f\n',n(jj));
end
%-----------------RK算法--------------------------
function z = myrungekutta(f, xx, z0)
n = length(xx);
h = xx(2)-xx(1);
z(1,:)=z0;
for i = 1:n-1
k1 = f(xx(i), z(i,:));
k2=f(xx(i)+0.5*h, z(i,:) + h*k1/2);
k3=f(xx(i)+0.5*h, z(i,:) + h*k2/2);
k4=f(xx(i)+h, z(i,:) + h*k3);
z(i+1,:)=z(i,:) + (k1 + 2*k2 + 2*k3 + k4)*h/6;
end