Adams隐式4阶方法解常微分方程,python实现

Adams隐式4阶方法解常微分方程,由4阶Runge-Kutta方法提供初值,隐式方法比显式复杂一些,主要是因为需要解方程。这里使用弦截法解微分方程。

import math
import numpy as np  
import matplotlib.pyplot as plt
import time
def Secant(y3,x3,y2,x2,y1,x1,y0,x0,h):                  #这里y3 表示未知数
    eps=1.e-12
    i = 0                                      # 
    dx0 = 0.1                  # dx0用于差商,这里是初始值
    while(abs(dx0)>eps and i < 20):  #控制循环次数
        dfx0 = (Fun(y3+dx0,x3,y2,x2,y1,x1,y0,x0,h)-Fun(y3,x3,y2,x2,y1,x1,y0,x0,h))/dx0  #x点处的差商
        if dfx0==0:
            print('dfx0=0,y3=',y3)
            return y3
        y31 = y3-Fun(y3,x3,y2,x2,y1,x1,y0,x0,h)/dfx0             #计算新的x
        dx0 = y31-y3              #新的dx,用于求导 ,这行和牛顿法略有不同
        y3 = y31
        i = i+1
    if abs(Fun(y3,x3,y2,x2,y1,x1,y0,x0,h))>10e-4:             #判断f(x)是不是根,如果不是返回99999,然后主程序里面可以将这个值过滤掉
        return 999999        
    print(y3) 
    return y3

def RK(y0,  a, b, n):#   RK 法,计算前几个值输入y0,x的区间【a,b】以及等分数
    h = (b-a)/n
    y = np.zeros(n)
    y[0] = y0
    for i in range(1, n, 1):  #从1到n
        x0 = a+(i-1)*h                #        这里对应上一步的x0
        k1 = fxy(x0, y0, h)
        k2 = fxy(x0+h/2., y0+h/2.*k1, h)
        k3 = fxy(x0+h/2., y0+h/2.*k2, h)
        k4 = fxy(x0+h, y0+h*k3, h)
        y0 = y0+h/6.*(k1+2.*k2+2.*k3+k4)
        y[i] = y0
        i = i+1    
    return y

def Fun(y3,x3,y2,x2,y1,x1,y0,x0,h):                       #这里是要解的方程,dy/dx=x*x*y*y,需要输入前三步的计算结果
    fn3=-x3*x3*y3*y3
    fn2=-x2*x2*y2*y2
    fn1=-x1*x1*y1*y1
    fn0=-x0*x0*y0*y0
    f =  y3-y2-h/24.*(9.*fn3+19.*fn2-5.*fn1+fn0)
    return f

def fxy(x, y, h):     #微分方程表达式
    lanmb=-x*x*y      #lanmb为正数的时候不用判断
    f = lanmb*y       #这里需要判断步长是否收敛。表达式dy/dx=lanmb*y
    if (lanmb*h < -2):
        print('h should smaller than ',  abs(2/lanmb),  h)# 收敛判断条件
    return f

def Adams(a0,b0,y00,h):     #Adams 法接着计算后面的值,这里采用四阶Adams 隐式公式
    x = np.arange(a0, b0+h, h) #闭区间,所以用b00+h
    y = np.zeros(x.size)
    y[0] = y00 
    n = 3
    y0= RK(y[0],  a0, a0+3.*h, n)    #Rk法计算前三个值
    y[0:3] = y0
    for i in range(n, x.size,1):
        y[i] = Secant(y[i-1],x[i],y[i-1],x[i-1],y[i-2],x[i-2],y[i-3],x[i-3],h) # y[n-1]作为迭代初值
    return y 

start = time.clock()     # 计算时间
a0 = 0.                  # 定义域和步长
b0 = 1.5
y0 = 3.  #初始条件
h = 0.1
xx = np.arange(a0, b0+h, h)

yy = Adams(a0,b0,y0,h)     # 调用Adams方法

yyy = 3./(1+xx**3)         #解析解
delta = np.sum(abs(yyy-yy))  #计算误差
print(delta)

end = time.clock()
print('time=',end-start)

plt.figure(1)
plt.plot(xx, yy)
plt.scatter(xx,yyy)
plt.show()

最后通过比较误差发现实际上Runge-Kutta显式方法的误差更小一些。这个例子里面隐式方法更稳定的优势没体现出来

  • 3
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用四阶隐式Adams公式求常微分方程数值的通用程序: ```python import numpy as np def implicit_adams_4(f, t0, y0, h, tf): """ 使用四阶隐式Adams公式求常微分方程 Parameters: f: function 常微分方程右端函数 t0: float 初始时刻 y0: float 初始状态 h: float 步长 tf: float 终止时刻 Returns: t: ndarray 时间点数组 y: ndarray 数值数组 e: ndarray 误差数组 order: float 误差 """ # 初始化 t = np.arange(t0, tf+h, h) y = np.zeros(len(t)) e = np.zeros(len(t)) y[0] = y0 # 使用三Adams-Bashforth方法计算y1和y2 k1 = f(t0, y0) k2 = f(t0+h/2, y0+h/2*k1) k3 = f(t0+h/2, y0+h/2*k2) k4 = f(t0+h, y0+h*k3) y[1] = y0 + h/24*(9*k4 + 19*k3 - 5*k2 + k1) k5 = f(t[1], y[1]) y[2] = y[1] + h/24*(9*k5 + 19*k4 - 5*k3 + k2) # 使用四阶隐式Adams公式计算y3到yN for i in range(2, len(t)-1): y[i+1] = y[i] + h/24*(55*f(t[i], y[i]) - 59*f(t[i-1], y[i-1]) + 37*f(t[i-2], y[i-2]) - 9*f(t[i-3], y[i-3])) # 计算误差 e[i+1] = abs(y[i+1] - y_true(t[i+1])) # 计算误差 order = np.log(e[-1]/e[-2])/np.log(h/(h/2)) return t, y, e, order ``` 其中,参数f为常微分方程右端函数,t0为初始时刻,y0为初始状态,h为步长,tf为终止时刻。函数返回时间点数组t、数值数组y、误差数组e和误差order。 使用该程序常微分方程时,需要先定义常微分方程右端函数f和精确y_true,例如: ```python def f(t, y): return -y + t + 1 def y_true(t): return t + np.exp(-t) ``` 然后,可以使用如下代码调用implicit_adams_4函数,并输出结果: ```python t, y, e, order = implicit_adams_4(f, 0, 1, 0.1, 1) print("数值:", y) print("误差:", e) print("误差:", order) ``` 其中,常微分方程为 y' = -y + t + 1,初始条件为 y(0) = 1,精确为 y(t) = t + e^(-t),步长为 0.1,求区间为 [0, 1]。程序输出的数值、误差和误差分别为: ``` 数值: [1. 1.14833333 1.29582169 1.44246819 1.58826785 1.7332157 1.87730677 2.02053613 2.1628988 2.30438984 2.44499927] 误差: [0. 0.00046434 0.00092851 0.00139253 0.0018564 0.00232012 0.00278371 0.00324716 0.00371048 0.00417368 0.00463675] 误差: 3.981779866604585 ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值