Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Cprocc/ScienceComputing

Open more actions menu

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

18 Commits
18 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

矩阵与数值分析实验作业


1. 9阶多项式拟合问题

  • 用九阶多项式拟合曲线,代码如下

    import numpy as np
    import matplotlib.pyplot as plt
    
    x_array = np.arange(1994, 2004, 1)
    y_array = np.array([67.052, 68.008, 69.803, 72.024, 73.400, 72.063, 74.669, 74.487, 74.065, 76.777])
    m = 10
    
    # 定义插值基函数l
    def l(i, x):
        temp0 = 1.0
        temp1 = 1.0
        for k in range(m):
            if k != i:
                temp0 *= (x - x_array[k])
                temp1 *= (x_array[i] - x_array[k])
        return temp0/temp1
    
    # 最终生成的多项式p
    def p(x):
        temp = 0.0
        for i in range(m):
            temp += y_array[i]*l(i, x)
        return temp
    
    
    z1 = np.polyfit(x_array, y_array, 9)
    x = np.linspace(1993.5, 2003.5, 100000)
    y_val = p(x)
    plot1 = plt.plot(x_array, y_array, '*', label='original values')
    plot2 = plt.plot(x, y_val, "r")
    
    # 预估2010年的石油量
    print(p(2010))
    plt.savefig("1-9阶多项式拟合.png")
  • 拟合图像如下:

1-9阶多项式拟合

  • 结论
    1. 当2010年时,预估的数据为-1951646.1340000005
    2. 从图像也可以看出,用Lagrange方法,对该10个数据进行插值时,出现了非常严重的Runge现象,因此高次多项式的插值拟合,并不能很好的描绘曲线

2. 最小二乘法拟合直线,抛物线,三次曲线问题

  • 用最小二乘法拟合离散数据

    from scipy.optimize import leastsq
    import matplotlib.pyplot as plt
    import numpy as np
    
    x_array = np.arange(1994, 2004, 1)
    y_array = np.array([67.052, 68.008, 69.803, 72.024, 73.400, 72.063, 74.669, 74.487, 74.065, 76.777])
    
    def aim_func1(p, x):
        """
        一次多项式拟合的方程
        """
        a1, a0 = p
        return a1*x + a0
    
    def aim_func2(p, x):
        """
        二次多项式(曲线)拟合的方程
        """
        a2, a1 , a0 = p
        return a2*(x**2) + a1*x + a0
    
    def error1(p, x, y):
        return aim_func1(p, x)-y
    
    def error2(p, x, y):
        return aim_func2(p, x)-y
    
    p1 = [0, 0]
    p2 = [0, 0, 0]
    Para1 = leastsq(error1, p1, args=(x_array, y_array))
    Para2 = leastsq(error2, p2, args=(x_array, y_array))
    a11, a10 = Para1[0]
    
    def f1(x):
        return a11*x + a10
    mean1 = 0
    mean_square_error1 = 1
    for i in range(len(x_array)):
        mean1 += (f1(x_array[i]) - y_array[i])**2
        mean_square_error2 = mean1**0.5
    print("拟合为直线时的均方差: " + str(mean_square_error1))
    print("估计2010年的值为: " + str(f1(2010)))
    
    a22, a21, a20 = Para2[0]
    
    
    def f2(x):
        return a22*(x**2) + a21*x + a20
    mean2 = 0
    mean_square_error2 = 1
    for i in range(len(x_array)):
        mean2 += (f2(x_array[i]) - y_array[i])**2
        mean_square_error2 = mean2**0.5
    print("拟合曲线为抛物线时的均方差: " + str(mean_square_error2))
    print("估计2010年的值为: " + str(f2(2010)))
    
    
    def three_times_fit():
        # 初始化
        x_array = np.arange(4, 14, 1)
        y_array = np.array([67.052, 68.008, 69.803, 72.024, 73.400, 72.063, 74.669, 74.487, 74.065, 76.777])
        m = len(x_array)
        a = np.zeros((4, 4), dtype='float64')
        b = np.zeros((4, 1))
    
        def f(i, x):
            """
            基函数span{1,x,x^2,x^3}
            """
            if i == 0:
                return 1
            elif i == 1:
                return x
            elif i == 2:
                return x ** 2
            else:
                return x ** 3
    
        def compute_eq():
            """
            计算法方程组的两个相关矩阵
            并求出稀疏矩阵res
            :return:
            """
            for i in range(4):
                for j in range(4):
                    for x in x_array:
                        a[i][j] += f(i, x) * f(j, x)
    
            for i in range(4):
                for j in range(len(y_array)):
                    b[i] += y_array[j] * f(i, x_array[j])
    
            a_inv = np.linalg.inv(a)
            res = np.dot(a_inv, b)
            return res
    
        res = compute_eq()
    
        def fit_y(x_r):
            x = x_r - 1990
            return res[0] + res[1] * x + res[2] * (x ** 2) + res[3] * (x ** 3)
    
        x = np.linspace(1994, 2006, 100000)
        plt.plot(x, fit_y(x), color="black", label="y=ax^3+bx^2+cx+d")
    
        def mean_square_error(x_array, y_array, y):
            mean_error = 0
            for i in range(len(x_array)):
                mean_error += (y_array[i] - y(x_array[i]+1990))**2
                return mean_error**0.5
    
        mean_square_error3 = mean_square_error(x_array, y_array, fit_y)
        print("拟合三次的均方差是: " + str(float(mean_square_error3)))
        fit2010 = fit_y(2010)
        print("估计2010年的值为: " + str(float(fit2010)))
    
    
    plt.figure(figsize=(8, 6))
    three_times_fit()
    plt.scatter(x_array, y_array, color="red", label="Sample Point")
    x = np.linspace(1994, 2006, 100000)
    y2 = a22*(x*x) + a21*x + a20
    plt.plot(x, y2, color="orange", label="y=ax^2+bx+c")
    y1 = a11*x + a10
    plt.plot(x, y1, color="blue", label="ax+b")
    
    plt.legend()
    plt.savefig("2-最小二乘法拟合123次曲线.png")
  • 拟合图像如下:

  • 运行结果如下:

    拟合类型 直线 抛物线 三次曲线
    均方差 1 2.6015334175442373 0.5064517482791047
    2010年的预估值 83.38227310285265 74.42525513470173 99.04197715791088
  • 结论:

    1. 从均方差的角度来看,三次曲线拟合的结果最好

3. 牛顿法解方程

  • 代码如下

    """
    牛顿迭代法求解方程组x^3+x^2+x-3=0 的根,初始值为x0 = -0.7
    迭代7步,并于真值 x*=1 比较
    """
    
    x_true = 1
    
    def f(x):
        """
        y=x^3+x^2+x-3
        :param x:
        :return:y
        """
        return x**3 + x**2 + x - 3
    
    
    def f_derivatives(x):
        """
        :param x:
        :return: y的导数值
        """
        return 3*(x**2) + 2*x + 1
    
    
    def f_derivatives2(x):
        """
        :param x:
        :return: y的二阶导数
        """
        return 6*x + 2
    
    
    def newton_iteration(x):
        """
        牛顿迭代法
        :param x:
        :return:
        """
        return x - f(x) / f_derivatives(x)
    
    
    if __name__ == '__main__':
        xk = -0.7
        ei_s = []
        for i in range(1, 8):
            print(" "*20 + "第 %s 次迭代" % i + " "*20)
            xk = newton_iteration(xk)
            print("xk的值= " + str(xk))
            ei = abs(xk - x_true)
            print("ei = |xi - x*|= " + str(ei))
            ei_s.append(ei)
            if i != 1:
                print("ei / (last_ei)^2 的值= " + str(ei_s[i-1]/(ei_s[i-2])**2))
            print("")
    
        print("f(x∗)的二阶导/2(f(x∗)的导数)= " + str(f_derivatives2(x_true)/(2*f_derivatives(x_true))))
  • 迭代结果如下

    迭代次数 x(i) 的值 e(i) =xi - x* e(i)/(e(i-1))^2
    1 2.6205607476635517 1.6205607476635517 -
    2 1.7084401902625614 0.7084401902625614 0.2697568987412368
    3 1.2063786980181086 0.2063786980181086 0.4112050941909951
    4 1.024161664125103 0.02416166412510301 0.5672795217855637
    5 1.0003814911210203 0.00038149112102026095 0.6534776653306854
    6 1.0000000969928142 9.699281422470563e-08 0.6664547866875559
    7 1.0000000000000062 0.6608747146171305 0.6666666666666666
  • 结论:

    • 从e(i) / (e(i-1))^2的值可以看出,方程是2阶收敛的。
    • 从f(x∗)的二阶导/2(f(x∗)的导数)= 0.6666666666666666可以看出,e(i)/ (e(i-1))^2是收敛到常数0.666..的,而这个值收敛到常数,也恰好满足迭代法2阶收敛的定义。

4. 有方程组 Hx=b, H为n*n阶 Hilbert矩阵,b为n维单位列向量。n=4,8

  • 代码如下

    import numpy as np
    import copy
    
    
    def gauss(n):
        l = np.zeros((n, n))
        u = np.zeros((n, n))
        a = np.zeros((n, n))
        b = np.ones((n, 1))
    
        # 初始化系数矩阵A
        for i in range(n):
            for j in range(n):
                a[i][j] = 1/(i+1+j+1-1)
    
        # 用Doolittle公式求解l,和u的系数
        for j in range(n):
            u[0][j] = a[0][j]
        for j in range(1, n):
            l[j][0] = a[j][0]/u[0][0]
        for d in range(n):
            l[d][d] = 1
    
        for i in range(1, n):
            for j in range(i, n):
                temp = 0
                for k in range(i):
                    temp += l[i][k] * u[k][j]
                u[i][j] = a[i][j] - temp
    
            for j in range(i+1, n):
                temp = 0
                for k in range(i):
                    temp += l[j][k] * u[k][i]
                l[j][i] = (a[j][i]-temp)/u[i][i]
    
        u_inv = np.linalg.inv(u)
        l_inv = np.linalg.inv(l)
        x = np.dot(u_inv, l_inv)
        x = np.dot(x, b)
        return x
    
    
    def jacobi(n):
        a = np.zeros((n, n))
        b = np.ones((n, 1))
        # 系数矩阵A初始化
        for i in range(n):
            for j in range(n):
                a[i][j] = 1/(i+1+j+1-1)
        # 初始解x0 = (0,0,0...共n个0)
        x1 = np.array([[0.0], [0.0], [0.0], [0.0]])
        for _ in range(200):
            x = copy.copy(x1)
            for i in range(n):
                temp = 0
                for j in range(n):
                    if i != j:
                        temp += a[i][j]*x[j][0]
                x1[i][0] = (b[i][0] - temp)/a[i][i]
        return x1
    
    
    def gauss_seidel(n):
        a = np.zeros((n, n))
        b = np.ones((n, 1))
        # 系数矩阵A初始化
        for i in range(n):
            for j in range(n):
                a[i][j] = 1/(i+1+j+1-1)
        # 初始解x0 = (0,0,0...共n个0)
        x = np.ones((n, 1))
        for _ in range(7000):
            for i in range(n):
                temp = 0
                for j in range(n):
                    temp += a[i][j]*x[j][0]
                x[i][0] = x[i][0] + (b[i][0] - temp)/a[i][i]
        return x
    
    
    def inner_product(a, b):
        temp = 0
        for i in range(len(a)):
            temp += a[i]*b[i]
        return temp
    
    
    def c_g(n):
        a = np.zeros((n, n))
        b = np.ones((n, 1))
        # 系数矩阵A初始化
        for i in range(n):
            for j in range(n):
                a[i][j] = 1 / (i + 1 + j + 1 - 1)
        # 初始解x0 = (0,0,0...共n个0)
        r0 = np.zeros((n, 1))
        x = np.ones((n, 1))
        r = b - np.dot(a, x)
        p = r
        time = 0
        while True:
            if (r == r0).all():
                print("C-G法迭代了 " + str(time) + " 次 ")
                return x
            else:
                ap = np.dot(a, p)
                if inner_product(p, ap) == 0:
                    print("C-G法迭代了 " + str(time) + " 次 ")
                    return x
                else:
                    alpha = inner_product(r, r)/(inner_product(p, ap))
                    x1 = x + alpha*p
                    r1 = r - alpha*ap
                    beat = inner_product(r1, r1)/inner_product(r, r)
                    p1 = r1 + beat*p
            r = copy.copy(r1)
            x = copy.copy(x1)
            p = copy.copy(p1)
            time += 1
    
    
    if __name__ == '__main__':
        print(gauss(4))
        print(gauss(8))
        # jacobi(4)
        # jacobi(8)
        print(gauss_seidel(4))
        print(gauss_seidel(8))
        print(c_g(4))
        print(c_g(8))
  • 结论

    1. 用Gauss法求解线性方程组:
      • n=4时,求出精确解 [-4, 60, -180, 140]
      • n=8时,求出精确解 [-8.00000121e+00, 5.04000060e+02, -7.56000074e+03, 4.62000038e+04, -1.38600010e+05, 2.16216013e+05, -1.68168009e+05, 5.14800026e+04]
    2. 用Jacobi法求解:
      • 不收敛,python报错栈溢出。尝试解简单方程可以解证明算法本身无误。
    3. 用Gauss-Seidel法求解:
      • n=4时,迭代7000次左右求出解 [-3.99143925, 59.90802849, -179.78427343, 139.86209779]
      • n=8时,迭代很慢,10万次迭代没有求解完成
    4. 用共轭梯度法求解:
      • n=4时,共迭代54次,求出精确解[-4, 60, -180, 140]
      • n=8时,共迭代664次,求出精确解 [-8.00000121e+00, 5.04000060e+02, -7.56000074e+03, 4.62000038e+04, -1.38600010e+05, 2.16216013e+05, -1.68168009e+05, 5.14800026e+04]

5. 编程计算三次样条S, 满足S(0)=1,S(1)=3,S(2)=3,S(3)=4,S(4)=2,其中边界条件为S''(0)=S''(4)=0

  • 代码如下

    import numpy as np
    from sympy import *
    
    
    # 初始化矩阵, n为阶数, Am=g即为要求的最终形式, lambda1, miu为中间变量
    n = 4
    x = np.array([0, 1, 2, 3, 4])
    y = np.array([1, 3, 3, 4, 2])
    A = np.zeros((5, 5))
    lambda1 = np.zeros(5)
    miu = np.zeros(5)
    h = np.zeros(5)
    g = np.zeros(5)
    
    # 根据课本180/181 页公式初始化g, A
    for i in range(1, n):
        lambda1[i] = 0.5
        miu[i] = 0.5
    
    for i in range(n):
        h[i] = 1
    
    g[0] = 6
    g[4] = -6
    for i in range(1, n):
        g[i] = 3*(miu[i]*(y[i+1]-y[i])/h[i] + lambda1[i]*(y[i]-y[i-1])/h[i-1])
    
    A[0][0] = 2
    A[0][1] = 1
    A[4][3] = 1
    A[4][4] = 2
    for i in range(1, n):
        A[i][i] = 2
        A[i][i-1] = lambda1[i]
        A[i][i+1] = miu[i]
    
    # 解出m
    A_inv = np.linalg.inv(A)
    m = np.dot(A_inv, g)
    
    # 确定n个区间内的函数
    xx = Symbol('x')
    for i in range(n):
        s = ((h[i]+2*(xx - x[i+1]))/(h[i]**3))*((xx - x[i+1])**2)*y[i] + \
            ((h[i]-2*(xx - x[i+1]))/(h[i]**3))*((xx - x[i])**2)*y[i+1] + \
            (xx - x[i])*((xx-x[i+1])**2)*m[i]/(h[i]**2) + (xx - x[i+1])*((xx-x[i])**2)*m[i+1]/(h[i]**2)
        print(s)
  • 运行结果如下:

    3x2(-2.0x + 3.0) + 0.678571428571428x2(x - 1) + 2.66071428571429x*(x - 1)2 + (x - 1)2 (2.0*x - 1.0)

    3*(-2.0x + 5.0)(x - 1)2 + 0.678571428571428*(x - 2)2(x - 1) + 3(x - 2)2(2.0x - 3.0) + 0.625*(x - 2)*(x - 1)2

    4*(-2.0x + 7.0)(x - 2)2 + 0.625*(x - 3)2(x - 2) + 3(x - 3)2(2.0x - 5.0) - 0.178571428571429*(x - 3)*(x - 2)2

    2*(-2.0x + 9.0)(x - 3)2 - 0.178571428571429*(x - 4)2(x - 3) + 4(x - 4)2(2.0x - 7.0) - 2.91071428571429*(x - 4)*(x - 3)2

  • 结论:

    1. 上述四个方程即分别对应[0,1],[1,2],[2,3],[3,4]四个区间的样条插值结果。

6. 取不同的初值,并使用弦截法,来计算方程组y=x^3 + 2x^2 + 10*x - 100的根

  • 首先,画图判断,根的个数为1,故可以用简单的弦截法来求根,图像代码如下:

    """
    用不同的初始值,并使用弦截法,来计算方程组y=x^3 + 2x^2 + 10x - 100的根
    """
    
    import matplotlib.pyplot as plt
    import numpy as np
    
    
    plt.figure(figsize=(8, 6))
    x = np.linspace(-2, 4, 1000000)
    y = x**3 + 2*(x**2) + 10*x - 100
    plt.plot(x, y, color="orange", label="y=x^3 + 2x^2 + 10x - 100")
    plt.legend()
    plt.savefig("6-弦截法求方程的实根-画图判别个数.png")
    
    """
    经画图判别上述方程只有一个实根(且实根在3.5附近),可用简单的弦截法计算。
    """
    
    
    def f(x):
        return x**3 + 2*(x**2) + 10*x - 100
    
    
    def secant_method(x0, x1):
        return x1 - f(x1)/(f(x1) - f(x0))*(x1 - x0)
    
    
    def re_secant_method(x0, x1):
        time = 0
        while True:
            temp = secant_method(x0, x1)
            x0 = x1
            x1 = temp
            time += 1
            if abs(f(x1)) - 0 < 0.00001:
                print("迭代了 " + str(time) + " 次")
                print(x1)
                break
    
    
    if __name__ == '__main__':
        print("当初始值选5, 4时 ")
        re_secant_method(5, 4)
        print("当初始值选100, 99时 ")
        re_secant_method(100, 99)
        print("当初始值选取-100, 50时 ")
        re_secant_method(-100, 50)
        print("当初始值选取100000, 200000时 ")
        re_secant_method(100000, 200000)
  • 结论

    初值选取(x0,x1) 迭代次数
    (5, 4) 5
    (-100, 50) 13
    (100, 99) 16
    (100000, 2000000) 42
    1. 当精度为1*10^(-5)时,迭代次数如上表所示。
    2. 可以看出,弦截法求方程根时,初值的选取很重要。

7. f(x)=e^(3x)cos(Pi*x),考虑f(x)在(0,2pi)上的积分。区间等分为50,100,200,500,1000

分别用复合梯形公式以及复合Simpson积分公式计算积分值,与精确值比较,列表说明误差的收敛性

  • 实验代码

    import numpy as np
    
    
    def f(x):
        return (np.exp(3*x))*(np.cos(np.pi*x))
    
    
    def composite_trapezoidal(n):
        """
        分成n份的复合梯形公式,区间为(0,2pi)
        :param n:
        :return:
        """
        a = 0
        b = 2*np.pi
        h = (b-a)/n
        temp = 0
        for i in range(1, n):
            temp += f(h*i)
        return ((b-a)/(2*n))*(f(a)+f(b)+2*temp)
    
    
    def composite_simpson(n):
        """
        复合Simpson
        """
        a = 0
        b = np.pi*2
        h = (b-a)/n
        temp1 = 0
        temp2 = 0
        for i in range(1, n):
            temp1 += f(h*i)
        for i in range(0, n):
            temp2 += f((0.5+i)*h)
        return ((b-a)/(6*n))*(f(a)+2*temp1+4*temp2+f(b))
    
    
    if __name__ == '__main__':
        test_n = [50, 100, 200, 500, 1000]
        for i in range(len(test_n)):
            print("取等分区间n= " + str(test_n[i]) + " 时的积分结果")
            print(composite_trapezoidal(test_n[i]))
            print(composite_simpson(test_n[i]))
  • 实验结果

    等分区间 复合梯形 复合辛普森
    50 35125341.19493705 35231407.86833493
    100 35204891.19998546 35232416.237822525
    200 35225534.978363276 35232479.16807642
    500 35231369.36592426 35232483.25445204
    1000 35232204.782320105 35232483.355091184
    精确解 35232483.36180031, 误差为0.015568195864034351
  • 结论

    1. 复合梯形公式和复合Simpson都是收敛到精确解的。
    2. 复合Simpson收敛速度更快,也对应了复合Simpson的收敛阶更高这一理论。

8. 分别用2点,3点,5点Gauss型积分公式计算如下定积分

  • 代码如下

    """
    本例采用Gauss-legendre方法构造Gauss积分
    Gauss求积分的步骤
    1. 求积区间和权函数构造n+1次正交多项式
    2. 解出正交多项式的 n+1个零点作为插值节点
    3. 求解n次代数精度得到的n+1个线性方程
    """
    import math
    
    # 被积函数1
    def fun1(x):
        return x**2/((1-x**2)**0.5)
    
    # 被积函数2
    def fun2(x):
        return math.sin(x)/x
    
    
    def main(fun, a, b):
        fun = fun
        a = a
        b = b
        # 构造对应的legendre多项式的求积节点
        g_l_2 = {0.5773502692: 1}
        g_l_3 = {0.7745966692: 0.555555556, 0: 0.8888888889}
        g_l_5 = {0.9061798459: 0.2369268851, 0.5384693101: 0.4786286705, 0: 0.5688888889}
    
        items = ['g_l_2', 'g_l_3', 'g_l_5']
    
        for i in range(len(items)):
            gauss_sum = 0.0
            functions = [g_l_2, g_l_3, g_l_5]
            for key, value in functions[i].items():
                gauss_sum += fun(((b - a) * key + a + b) / 2) * value
                if key > 0:
                    gauss_sum += fun(((a - b) * key + a + b) / 2) * value
            gauss_sum = gauss_sum * (b - a) / 2
            print(items[i], ":   ", gauss_sum)
    
    
    if __name__ == '__main__':
        print("计算函数1的结果 ")
        main(fun1, -1, 1)
        print("计算函数2的结果 ")
        main(fun2, 0, math.pi*0.5)
  • 运行结果:

    定积分1 定积分2
    2点 0.8164965809644047 1.3704190473423845
    3点 1.0540925540351531 1.3707634378263132
    5点 1.2495037445030532 1.370762168226821
    精确解 1.570796326794678 1.3707621681544881
  • 结论:

    1. 可以看出两个定积分都是逐步收敛到精确解的。
    2. 不同的积分形式收敛速度也不一样。
    3. 同一个积分点数越多越精确。理论上的节点越多,Gauss型求积公式的代数精度越高符合。

9. 用Euler,改进的Euler,Runge-Kutta法,步长分别为0.1, 0.01, 0.001求解方程

  • 代码如下:

    """
    y'=y(cost)
    y(0)=1
    """
    import math
    import matplotlib.pyplot as plt
    import numpy as np
    
    
    # 定义y的导数
    def y_derivatives(y, t):
        return y*math.cos(t)
    
    
    # euler法求微分方程
    def euler():
        h = [0.1, 0.01, 0.001]
        for h1 in h:
            N = 1/h1
            u = [0 for _ in range(int(N+1))]
            t = [0 for _ in range(int(N+1))]
            u[0] = math.cos(t[0])*1
            for i in range(0, int(N)):
                t[i] = i*h1
                u[i+1] = u[i] + h1*y_derivatives(u[i], t[i])
            plt.scatter(1, u[int(N)], color="red", label="euler")
            print("当步长设置为: " + str(h1) + " Euler法的结果如下")
            print(u[int(N)])
    
    
    # 改进的euler方法求解微分方程
    def euler_plus():
        h = [0.1, 0.01, 0.001]
        for h1 in h:
            N = 1 / h1
            u = [0 for _ in range(int(N + 1))]
            t = [0 for _ in range(int(N + 1))]
            u[0] = 1
    
            for i in range(0, int(N+1)):
                t[i] = i*h1
    
            for i in range(0, int(N)):
                u_temp = u[i] + h1*y_derivatives(u[i], t[i])
                u[i+1] = u[i] + (h1*0.5)*(y_derivatives(u[i], t[i])+y_derivatives(u_temp, t[i+1]))
    
            plt.scatter(1, u[int(N)], color="orange", label="euler_plus")
            print("当步长设置为: " + str(h1) + " 改进的Euler法的结果如下")
            print(u[int(N)])
    
    
    # 四阶runge_kutta法
    def runge_kutta():
        h = [0.1, 0.01, 0.001]
        for h1 in h:
            N = int(1 / h1)
            u = [0 for _ in range(int(N + 1))]
            t = [0 for _ in range(int(N + 1))]
            u[0] = 1
            for i in range(N):
                t[i] = i * h1
                k1 = y_derivatives(u[i], t[i])
                k2 = y_derivatives(u[i]+0.5*h1*k1, t[i]+0.5*h1)
                k3 = y_derivatives(u[i]+0.5*h1*k2, t[i]+0.5*h1)
                k4 = y_derivatives(u[i]+h1*k3, t[i]+h1)
                u[i+1] = u[i]+(h1/6)*(k1+2*k2+2*k3+k4)
    
            print("当步长设置为: " + str(h1) + " runge_kutta法的结果如下")
            print(u[int(N)])
            plt.scatter(1, u[int(N)], color="blue", label="runge_kutta")
    
    
    if __name__ == '__main__':
        plt.figure(figsize=(8, 6))
        euler()
        euler_plus()
        runge_kutta()
    
        print("精确解: ", math.exp(math.sin(1)))
        # 绘图
        x = np.linspace(0, 1, 1000)
        yy = np.exp(np.sin(x))
        plt.plot(x, yy, color="black", label="y=e^sin(t)")
        plt.legend()
        plt.savefig("9-微分方程近似解.png")
  • 手动求出 y(t) 的原函数,和求出的近似值对比,画图如下:

  • 因为图像过于接近,补充表格说明:

    步长 Euler法 改进的Euler法 Runge-Kutta法(4阶)
    0.1 2.2882605537942076 2.3157635557244522 2.3197758575243266
    0.01 2.3166671961754823 2.319735792689209 2.319776824620262
    0.001 2.3194663522951275 2.3197764135860863 2.319776824715841
    精确解 2.319776824715853 ------------------------------- ------------------------------
  • 结论:

    1. 可以很清楚的看出,Runge-Kutta法误差最小。Euler误差最大。
    2. 同一种方法,步长越短,得到的值越精确,但是最终是收敛的,这也与我们的理论知识对应。

About

science computing homework

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

Morty Proxy This is a proxified and sanitized view of the page, visit original site.