非线性方程的数值解法-二分法,牛顿法,弦截法

1.二分法

1.1 定义

考 察 有 根 区 间 [ a , b ] , 取 中 点 x 0 = ( a + b ) / 2 将 它 分 成 两 半 , 考察有根区间[a,b],取中点x_0=(a+b)/2将它分成两半, [a,b],x0=(a+b)/2, 假 设 中 点 x 0 不 是 f ( x ) 的 零 点 , 然 后 进 行 根 的 搜 索 , 即 检 查 假设中点x_0不是f(x)的零点,然后进行根的搜索,即检查 x0f(x),,
f ( x 0 ) 与 f ( a ) 是 否 同 号 , 如 果 同 号 , 说 明 所 求 的 根 x ∗ 在 x 0 的 右 侧 , 这 时 令 a 1 = x 0 , b 1 = b ; 否 则 x ∗ 必 在 x 0 的 左 侧 , f(x_0)与f(a)是否同号,如果同号,说明所求的根x^*在x_0的右侧,这时令a_1=x_0,b_1=b;否则x^*必在x_0的左侧, f(x0)f(a),xx0a1=x0,b1=b;xx0,
这 时 令 a 1 = a , b 1 = x 0 , 对 新 的 有 根 区 间 [ a 1 , b 1 ] 施 行 同 种 过 程 , 每 次 二 分 后 , 设 取 有 根 区 间 [ a k , b k ] 的 中 点 这时令a_1=a,b_1=x_0,对新的有根区间[a_1,b_1]施行同种过程,每次二分后,设取有根区间[a_k,b_k]的中点 a1=a,b1=x0,[a1,b1],[ak,bk]
x k = ( a k + b k ) / 2 x_k=(a_k+b_k)/2 xk=(ak+bk)/2
作 为 根 的 近 似 值 , 则 在 二 分 过 程 中 可 以 获 得 一 个 近 似 根 序 列 作为根的近似值,则在二分过程中可以获得一个近似根序列 ,
x 0 , x 1 , x 2 , … , x k , … , x_0,x_1,x_2,\ldots,x_k,\ldots, x0,x1,x2,,xk,,
该 序 列 必 以 根 x ∗ 为 极 限 , 该 方 法 称 为 二 分 法 . 该序列必以根x^*为极限,该方法称为二分法. x,.

1.2 Python实现二分法

from sympy.abc import x
from sympy import init_printing, evalf, exp, solve, Eq

init_printing(use_unicode=True)


def bisection(f_x, interval_start: (float, int), interval_end: (float, int), epsilon=5e-3):
    """
    二分法求解单变量非线性方程组f(x)=0在区间[a,b]上的准确到小数点后指定精度的近似解
    :param f_x: 单变量非线性函数f(x)
    :param interval_start: 求根区间始点
    :param interval_end: 求根区间终点
    :param epsilon: 小数点精度
    :return: 近似解x,二分迭代次数k
    """
    if f_x.subs(x, interval_start) * f_x.subs(x, interval_end) < 0:
        # 初始化二分次数,区间长度
        bisection_count = 0
        interval_length = abs(interval_start - interval_end)
        while 1:
            middle_point = (interval_start + interval_end) / 2
            bisection_count += 1
            value = f_x.subs(x, middle_point)
            if interval_length <= epsilon or value == 0:  # 达到预定精度epsilon或者等于精确解时结束循环
                return middle_point, bisection_count
            if value * f_x.subs(x, interval_start) < 0:
                interval_end = middle_point
            else:
                interval_start = middle_point
            interval_length = abs(interval_start - interval_end)
    else:
        raise Exception("function has no solution in the specified interval")

2.牛顿法

2.1 定义

设 已 知 方 程 f ( x ) = 0 有 近 似 根 x k ( 假 定 f ′ ( x k ) ≠ 0 ) , 将 函 数 f ( x ) 在 点 x k 处 展 开 , 有 设已知方程f(x)=0有近似根x_k(假定f'(x_k)\neq 0),将函数f(x)在点x_k处展开,有 f(x)=0xk(f(xk)=0),f(x)xk,
f ( x ) ≈ f ( x k ) + f ′ ( x k ) ( x − x k ) , f(x)\approx f(x_k)+f'(x_k)(x-x_k), f(x)f(xk)+f(xk)(xxk),
于 是 方 程 f ( x ) = 0 可 近 似 地 表 示 为 于是方程f(x)=0可近似地表示为 f(x)=0
f ( x k ) + f ′ ( x k ) ( x − x k ) = 0. f(x_k)+f'(x_k)(x-x_k)=0. f(xk)+f(xk)(xxk)=0.
这 是 个 线 性 方 程 , 记 其 根 为 x k + 1 , 则 x k + 1 的 计 算 公 式 为 这是个线性方程,记其根为x_{k+1},则x_{k+1}的计算公式为 线,xk+1xk+1
x k + 1 = x k − f ( x k ) f ′ ( x k ) , k = 0 , 1 , … , x_{k+1}=x_k-\frac{f(x_k)}{f'(x_k)},\quad k=0,1,\ldots, xk+1=xkf(xk)f(xk),k=0,1,,
这就是牛顿法

2.2 Python实现牛顿法

def newtown_tangent(f_x, x0, true_root, epsilon=5e-4):
    """
    牛顿法求解单变量非线性方程组f(x)=0在区间[a,b]上的准确到小数点后指定精度的近似解
    :param f_x: 单变量非线性函数f(x)
    :param x0: 迭代初始近似值
    :param true_root: 精确解
    :param epsilon: 小数点精度
    :return: 近似解x,二分迭代次数k
    """
    f = f_x.subs(x, x0)
    diff_f_x = f_x.diff()
    diff_f = diff_f_x.subs(x, x0)
    iteration_count = 0
    while 1:
        x_k = x0 - f / diff_f
        iteration_count += 1
        if abs(x_k - true_root) < epsilon:
            return x_k, iteration_count
        x0 = x_k
        f = f_x.subs(x, x0)
        diff_f = diff_f_x.subs(x, x0)

3.弦截法

3.1 定义

设 x k , x k + 1 是 方 程 f ( x ) = 0 的 近 似 根 , 设x_k,x_{k+1}是方程f(x)=0的近似根, xk,xk+1f(x)=0 利 用 f ( x k ) , f ( x k − 1 ) 构 造 一 次 插 值 多 项 式 p 1 ( x ) , 并 用 p 1 ( x ) = 0 利用f(x_k),f(x_{k-1})构造一次插值多项式p_1(x),并用p_1(x)=0 f(xk)f(xk1)p1(x),p1(x)=0
的 根 作 为 方 程 f ( x ) = 0 的 新 的 近 似 根 x k + 1 . 由 于 的根作为方程f(x)=0的新的近似根x_{k+1}.由于 f(x)=0xk+1.
p 1 ( x ) = f ( x k ) + f ( x k ) − f ( x k − 1 ) x k − x k − 1 ( x − x k ) , ( 1 ) p_1(x)=f(x_k)+\frac{f(x_k)-f(x_{k-1})}{x_k-x_{k-1}}(x-x_k),(1) p1(x)=f(xk)+xkxk1f(xk)f(xk1)(xxk),(1)
因 此 有 因此有
x k + 1 = x k − f ( x k ) f ( x k ) − f ( x k − 1 ) ( x k − x k − 1 ) , k = 0 , 1 , … , ( 2 ) x_{k+1}=x_k-\frac{f(x_k)}{f(x_k)-f(x_{k-1})}(x_k-x_{k-1}),\quad k=0,1,\ldots,(2) xk+1=xkf(xk)f(xk1)f(xk)(xkxk1),k=0,1,,(2)
这 样 导 出 的 迭 代 公 式 ( 2 ) 可 以 看 做 牛 顿 公 式 这样导出的迭代公式(2)可以看做牛顿公式 (2)
x k + 1 = x k − f ( x k ) f ′ ( x k ) x_{k+1}=x_k-\frac{f(x_k)}{f'(x_k)} xk+1=xkf(xk)f(xk)
中 导 数 f ′ ( x k ) 用 差 商 f ( x k ) − f ( x k − 1 ) x k − x k − 1 取 代 的 结 果 . 中导数f'(x_k)用差商\dfrac{f(x_k)-f(x_{k-1})}{x_k-x_{k-1}}取代的结果. f(xk)xkxk1f(xk)f(xk1).
该 方 法 称 为 弦 截 法 . 该方法称为弦截法. .

3.2 Python实现弦截法

def secant(f_x, x0, x1, true_root, epsilon=5e-4):
    """
    弦截法求解单变量非线性方程组f(x)=0在区间[a,b]上的准确到小数点后指定精度的近似解
    :param f_x: 单变量非线性函数f(x)
    :param x0: 第一个迭代初始近似值
    :param x1: 第二个迭代初始近似值
    :param true_root: 精确解
    :param epsilon: 小数点精度
    :return: 近似解x,二分迭代次数k
    """
    f_x0 = f_x.subs(x, x0)
    f_x1 = f_x.subs(x, x1)
    difference_quotient = (f_x1 - f_x0) / (x1 - x0)
    iteration_count = 0
    while 1:
        x_k = x1 - f_x1 / difference_quotient
        iteration_count += 1
        if abs(x_k - true_root) < epsilon:
            return x_k, iteration_count
        x0 = x1
        x1 = x_k
        f_x0 = f_x.subs(x, x0)
        f_x1 = f_x.subs(x, x1)
        difference_quotient = (f_x1 - f_x0) / (x1 - x0)

4.测试

if __name__ == '__main__':
    # 第一组测试函数,来源详见李庆扬数值分析第5版P214,e.g.2
    f = x ** 3 - x - 1
    appropriate_root1, iteration_count1 = bisection(f, 1.0, 1.5)
    print("二分法求方程近似解x={},迭代次数={}".format(appropriate_root1,iteration_count1))
    # 二分法测试区间中点恰好是精确解时的情况
    f = x - 1
    appropriate_root2, iteration_count2 = bisection(f, 0.0, 2.0)
    print("测试区间中点恰好是精确解时,近似解x={},迭代次数={}".format(appropriate_root2, iteration_count2))
    # 第二组测试函数,来源详见李庆扬数值分析第5版P238,e.g.7
    f = x ** 3 - 3 * x - 1
    initial_value = 2
    true_solution = 1.87938524
    root1, count1 = newtown_tangent(f, initial_value, true_solution)
    print("牛顿切线法近似解x={},迭代次数={}".format(root1.evalf(), count1))
    initial_value0, initial_value1 = 2, 1.9
    root2, count2 = secant(f, initial_value0, initial_value1, true_solution)
    print("弦截法近似解x={},迭代次数={}".format(root2.evalf(), count2))
    # 第三组测试函数,来源详见李庆扬数值分析第5版P224-225
    c = 115
    f = x ** 2 - c
    true_solution = pow(c, 0.5)
    initial_value = 11
    solution, count = newtown_tangent(f, initial_value, true_solution, epsilon=1e-6)
    print("牛顿切线法近似解x={},迭代次数={}".format(solution.evalf(), count))
    # 第四组测试函数,来源详见李庆扬数值分析第5版P229 e.g.10
    f = x * exp(x) - 1
    root = solve(Eq(f, 0), x)[0]
    print("调用sympy函数库解x={}".format(root))
    initial_value0, initial_value1 = 0.5, 0.6
    root, count = secant(f, initial_value0, initial_value1, root)
    print("牛顿切线法近似解x={},迭代次数={}".format(root.evalf(), count))

5.运行结果

在这里插入图片描述

  • 2
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值