欧拉法求解微分方程

by Conmajia
2014

原文是我在2014年写的,用C# 完成,这里改成JavaScript了,特基础。当然最方便的还是用数学库,或者Matlab、Mathematics这些数学软件(如果你只求值的话),或者可以换成C、Java、Go、Erlang任何其他的语言实现。欧拉(Euler)和中心差分逼近,是最朴素的想法,可惜代数精度太低了,而龙格库塔的稳定性又是个问题。总之只能用来计算普通的东西,高大上问题一般还是使用广义$\alpha$,Wilson-$\Theta$之类的,利用系数调节人工阻尼,让低频的部分少受算法影响,高频阻尼增大,增加微分方程的稳定性,又不影响算法的精度。

Math.NET计划是用于.NET的数学运算库,包括了数值计算库Iridium.NET。这是非常古老的的库,最近一次更新已经是10年前了。但是,数学的东西永远不会过时,所以仔细研究一下,还是可以吃到很多干货的。



$ git clone git://github.com/mathnet/mathnet-iridium.git

工程中有时候需要解微分方程,比如这种:

\[
y'=y-\cfrac{2x}{y}
\]

\(y(0)=1\). 两边积分,得到:

\[
\begin{align*}
\int_{x_n}^{x_{n+1}}y'\mathrm{d} x&=\int_{x_n}^{x_{n+1}}\left(y-\cfrac{2x}{y}\right)\mathrm{d} x \y_{n+1}-y_n&=\int_{x_n}^{x_{n+1}}\left(y-\cfrac{2x}{y}\right)\mathrm{d}x \y&=\sqrt{1+2x}
\end{align*}
\]

工程上只想要数值解,一般采用差分近似代替微分. 最简单最朴素的办法是用欧拉公式

\[
\tag{1}
y_{n+1}=y_n+\Delta x f(x_n,y_n)\quad n=0,1,2,\cdots
\]

推导很简单,对\(x_n\),有:

\[
y'_n=f(x_n,y_n).
\]

对\(y'(x_n)\)有:

\[
\tag{2}
y'_n\approx \cfrac{y_{n+1}-y_n}{\Delta x}.
\]

式(2)左边叫微商(不是喜提迪丽热巴的那个微商),右侧叫差商(不是淘宝伪劣产品卖家),\(\Delta x=\left|x_{n+1}-x_n\right|\).

代回式(2),有:

\[
\begin{align*}
y'_n=f(x_n,y_n) \Rightarrow\cfrac{y_{n+1}-y_n}{\Delta x} &\approx f(x_n,y_n) \y_{n+1}-y_n &\approx \Delta x f(x_n,y_n) \y_{n+1} &\approx y(x_n)+\Delta x f(x_n,y_n).
\end{align*}
\]

式(1)的欧拉公式成了:

\[
\tag{3}
y_{n+1}=y_n+\Delta x\left(y_n-\cfrac{2x_n}{y_n}\right).
\]

假设用myFn函数表示余项\(y_n-\dfrac{2x_n}{y_n}\):

function myFn(x, y) {
  return y - 2 * x / y;
}

Euler可以这样实现:

function calculate(x0, y0, delta, xn) {
  var yn;
  while(x0 < xn) {
    yn = y0 + delta * myFn(x0, y0);
    y0 = yn;
    x0 = x0 + delta;
  }
  return yn;
}

现在可以开始试验了。

理论上:

\[
y=\sqrt{1+2x}\Rightarrow y(1)=\sqrt{3}\approx 1.7321
\]

\(\sqrt{3}\)是方程的真值,程序的目标是通过计算,得到尽量接近真值的结果.

取\(x_0=0\),\(y_0=1\),\(\Delta x=0.1\),程序计算结果为:

ans = 1.784771

误差3.04%. 减小\(\Delta x\),比如\(\Delta x=0.0001\),计算结果为:

ans = 1.732112

误差0.0007%十分接近真值了.

虽然这个方法可以求到比较精确的解,但是\(\Delta x\)太小的话,while会执行很多次,效率低下.

引入定积分的梯形公式:

\[
\int_{x_n}^{x_{n+1}}f(x,y)\mathrm{d}x\approx\cfrac{\Delta x}{2}\left[f(x_n,y_n)+f(x_{n+1},y_{n+1})\right]
\]

式(3)可以写成:

\[
\tag{4}
y_{n+1}\approx y_n+\cfrac{\Delta x}{2}\left[f(x_n,y_n)+f(x_{n+1},y_{n+1})\right].
\]

式(3)和式(4)的特点是,前者速度快,精度低;后者速度慢,精度高. 所以对于粗算,可以使用:

\[
y'_{n+1}=y_n+\Delta x f(x_n,y_n)
\]

精算,可以用:

\[
y_{n+1}=y_n+\cfrac{\Delta x}{2}\left[f(x_n,y_n)+f(x_{n+1},y'_{n+1})\right].
\]

结合起来,先通过粗算得到\(y'_{n+1}\)的近似值,再进行精算,得到高精度的最终解. 这样的话,既保证了计算结果的准确度,又没有消耗太多的计算资源,保证了计算效率. 下面是改进后的计算程序:

function calculate(x0, y0, delta, xn) {
  var yp, yc;
  while(x0 < xn) {
    yp = y0 + delta * myFn(x0, y0);
    yc = y0 + delta * myFn(x0 + delta, yp);
    y0 = 1 / 2 * (yp + yc);
    x0 = x0 + delta;
  }
  return y0;
}

取\(x_0=0\),\(y_0=1\),\(\Delta x=0.1\),计算结果为:

ans = 1.737867

误差0.33%.

和最早版本(误差3.04%)相比,在\(\Delta x\)相同——意味着循环次数相近——的情况下,精度提高接近10倍.

具体问题具体分析,手工求微分方程基本是这么个思路. 当然,对于《数值分析》和《工程数学》这些课程来说,我上面写的东西不过是小儿科了. 更多的时候,做个伸手党其实很不错的,大把的现成玩意儿可以用,我干嘛还要费那劲呢?

The End. \(\Box\)

原文地址:https://www.cnblogs.com/conmajia/p/solve-differential-equation.html

时间: 2024-07-31 12:10:29

欧拉法求解微分方程的相关文章

多步法求解微分方程数值解

对于求解一个较大的系统来说,由于单步法所需要的信息较小导致精度不高,从而需要使用多步法来填补单步法的不足之处.常见的多步法有:Adams显示格式与Adams预测校正格式等. Matalb代码: 1.Adams方法 function [x,y]=Adams4(f1,tspan,h,x0,y0) x=(tspan(1):h:tspan(2))';%等分区间 n=length(x); y=zeros(n,1);%存储y值 x(1)=x0; y(1)=y0; %使用Rungekutta4方法,为Adam

matlab求解时滞微分方程

matlab求解时滞微分方程,dde23调用格式: sol = dde23(ddefun,lags,history,tspan); --ddefun函数句柄,求解微分方程y'=f(t,y(t),y(t-τ1),...,y(t-τk))         必须写成下面形式: dydt =ddefun(t,y,Z); 其中t对应当前时间t,y为列向量,近似于y(t):Z(:,j)近似于y(t-τj) --lags为延迟时间,为正常数. 例:方程中包含y1(t-0.2)和y2(t-1),则可以表示为la

解微分方程+ode求解器

该命令中可以用D表示微分符号,其中D2表示二阶微分,D3表示三阶微分,以此类推. 求精确解 1.微分方程 r=dsolve('eqn1','eqn2',...,'cond1','cond2',...,'var'). 解释如下:eqni表示第i个微分方程,condi表示第i个初始条件,var表示微分方程中的自变量,默认为t. >> dsolve('Dy=3*x^2','y(0)=2','x') ans =  x^3 + 2 2.微分方程组 >> [x,y]=dsolve('Dx=y'

三种初步简易的方法求解数值问题 of C++

1. “二分法解方程” 在二分法中,从区间[a,b]开始,用函数值f(a)与f(b)拥有相反的符号.如果f在这个区间连续,则f的图像至少在x=a,x=b之间穿越x轴一次,因此方程f(x)=0在[a,b]之间至少有一个解,通过逐步对[a,b]区间进行二分处理,选取在那一部分改变了符号,逐步缩小方程解的更小区域. 1 /************************************************************************/ 2 /*二分法 解方程 */ 3

构造了一种难解的非线性一阶常微分方程,边值特殊;但可用非常规方法求解

求解微分方程的方法是不是可以增添新类型了?

【游戏物理】欧拉、龙格、韦尔莱

简单介绍在游戏中模拟物理运动的三个常见方法. 欧拉方法 显式欧拉方法 在数学和计算机科学中,欧拉方法,命名自它的发明者莱昂哈德·欧拉,是一种一阶数值方法,用以对给定初值的常微分方程(即初值问题)求解.它是一种解决数值常微分方程的最基本的一类显型方法(Explicit method). 欧拉方法通过记录物体位置和速度,然后在每帧循环期间把速度累加到位置上,从而模拟物体的物理运动. 初中物理内容了,根据此刻速度和加速度,可计算出下一刻的速度和位移差. \[ v_{n+1} = v_n + a_ndt

傅里叶变换

http://blog.jobbole.com/70549/ 我保证这篇文章和你以前看过的所有文章都不同,这是 2012 年还在果壳的时候写的,但是当时没有来得及写完就出国了--于是拖了两年,嗯,我是拖延症患者-- 这篇文章的核心思想就是: 要让读者在不看任何数学公式的情况下理解傅里叶分析. 傅里叶分析不仅仅是一个数学工具,更是一种可以彻底颠覆一个人以前世界观的思维模式.但不幸的是,傅里叶分析的公式看起来太复杂了,所以很多大一新生上来就懵圈并从此对它深恶痛绝.老实说,这么有意思的东西居然成了大学

《自动化技术中的进给电气运动》及学科学习笔记二

<自动化技术中的进给电气运动> 阅读内容:第1.3节 知识要点: 本节主要以不可调节电气传动系统为例,介绍了系统在时间域的静态和动态特性以及电气系统对于简单信号的响应. 1.采用微分方程分析系统 对于只有一个输入和输出的线性系统都可表示成如下的微分方程形式.其中u为输入,v为输出,且对于实际系统有m≤n. 以不可调电气传动系统为例,列出系统的机械和电气微分方程. (1)JGes=JM+JL (2)uA-eM=RAiA+LAdiA/dt (3)eM=cMωM (4)MM=ML+MB=ML+JGe

[转]林达华推荐的几本数学书

http://blog.csdn.net/lqhbupt/article/details/32106217 Dahua Lin早在几年前就已经冒尖出来了,现在在MIT攻读博士学位,前途不可限量.他总是有无穷的精力,学习,同时几篇几篇的写paper,几万行几万行的写code,几万字几万字的写blog.他扎实的数学功底和相关知识的功底,以及深睿的洞察和理解问题的能力,注定他将在machine learning和computer vision等相关领域取得大量的成果,甚至是突破性的成果.期待他在这些领