[数值分析]牛顿迭代法Newton!!!

牛顿迭代法Newton!!!

牛顿迭代法的基本思想

牛顿迭代法是一种用来求解方程的方法,它的基本思想是:如果一个函数在某一点的切线是直线,那么迭代下一次产生的值就是切线与x轴的交点的x坐标,按照这个思想,我们可以不断迭代,直到收敛到方程的根。

牛顿迭代法的数学表达

设函数f(x)x0处可导,且f(x)x0处的一阶导数f(x0)存在,且f(x0)0,则称x0是方程f(x)=0的一个牛顿迭代点,记作x0R,且称函数f(x)x0处的切线方程为

f(x)=f(x0)+f(x0)(xx0)

x1是方程f(x)=0的一个牛顿迭代点,且x1满足

x1=x0f(x0)f(x0)

则称x1是方程f(x)=0的一个牛顿迭代点,记作x1R,且称函数f(x)x1处的切线方程为

f(x)=f(x1)+f(x1)(xx1)

x2是方程f(x)=0的一个牛顿迭代点,则x2也满足

x2=x1f(x1)f(x1)

所以,牛顿迭代法的迭代公式为

xn+1=xnf(xn)f(xn)

简单例子

求解方程x3x2x1=0的正根,ε=104

用牛顿迭代法

import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
def f(x):
    return x**3 - x**2 - x - 1
def f1(x):
    return 3*x**2 - 2*x - 1
def newton(x0, eps):
    x = x0
    while abs(f(x)) > eps:
        x = x - f(x)/f1(x)
    return x
newton(1, 1e-4)
newton(2, 1e-4)
from pickle import TRUE
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline


def f(x):
    return x**3 - x**2 - x - 1


def f1(x):
    return 3*x**2 - 2*x - 1


def newton(x0, eps,isprint=True):
    x = x0
    while abs(f(x)/f1(x)) > eps:
        x = x - f(x)/f1(x)
        if isprint:
            print(x)
    x = x - f(x)/f1(x)
    return x


# newton(1, 1e-4) 
x = np.linspace(-2, 3, 100)
plt.plot(x, f(x))
plt.plot(x, f1(x))
plt.grid()
# 显示坐标轴
plt.axhline(y=0, color='k')
plt.axvline(x=0, color='k')
<matplotlib.lines.Line2D at 0x2b08f696cd0>


png

f(1)
-2
f(2)
1
newton(2, 1e-4)
1.8571428571428572
1.839544513457557
1.8392868100680193





1.8392867552141636
tmpx = 1.839544513457557
f(tmpx)/f1(tmpx)
0.00025770338953776654

弦截法

def secant(x0, x1, eps):
    x = x1
    while abs(f(x)) > eps:
        x = x - f(x)*(x-x0)/(f(x)-f(x0))
        x0 = x1
        x1 = x
    return x
secant(1, 2, 1e-4)
def secant(x0, x1, eps, isprint=True):
    x = x1
    while abs(f(x)*(x-x0)/(f(x)-f(x0))) > eps:
        x = x - f(x)*(x-x0)/(f(x)-f(x0))
        x0 = x1
        x1 = x
        if isprint:
            print(x)
    x = x - f(x)*(x-x0)/(f(x)-f(x0))
    return x
secant(1, 2, 1e-4)
1.6666666666666667
1.816326530612245
1.8429939112321743
1.8392156332109642





1.839286537939871

小结

相比于弦截法,牛顿迭代法的收敛速度更快,但是牛顿迭代法需要求解一阶导数,而弦截法不需要。

但牛顿迭代法的一阶导数为零时,牛顿迭代法会出现问题,所以牛顿迭代法的收敛速度更快的前提是一阶导数不为零。

我们在后面的课程中会学习到牛顿迭代法的改进方法,这些改进方法可以解决牛顿迭代法一阶导数为零时的问题。

牛顿迭代法解方程组

简单例子

{f(x,y)=x+2y3=0g(x,y)=2x2+y25=0(x0,y0)=(1,2)

对上述方程组,我们求其迭代过程中的一阶展开式

[f(xn+1,yn+1)g(xn+1,yn+1)]=[fxfygxgy][xn+1xnyn+1yn]+[f(xn,yn)g(xn,yn)]

分别令f(xn+1,yn+1)=0g(xn+1,yn+1)=0,且记Δx=xn+1xnΔy=yn+1yn,则有

{f(xn,yn)+fxΔx+fyΔy=0g(xn,yn)+gxΔx+gyΔy=0

则我们的目标成为了解下述方程

(1)fxΔx+fyΔy=f(xn,yn)(2)gxΔx+gyΔy=g(xn,yn)

这是一个二元一次方程组,由高中知识我们可以很容易地解出ΔxΔy,从而得到xn+1yn+1

(3)Δx=gyf(xn,yn)fyg(xn,yn)fxgyfygx(4)Δy=fxg(xn,yn)gxf(xn,yn)fxgyfygx

又有xn+1=xn+Δxyn+1=yn+Δy,所以我们的迭代公式为

{xn+1=xnf(xn,yn)gy(xn,yn)g(xn,yn)fy(xn,yn)fx(xn,yn)gy(xn,yn)fy(xn,yn)gx(xn,yn)yn+1=ynfx(xn,yn)g(xn,yn)gx(xn,yn)f(xn,yn)fx(xn,yn)gy(xn,yn)fy(xn,yn)gx(xn,yn)

则对上述具体例子,我们便可以首先求解f(x,y)g(x,y)的偏导数,容易得到

(5)fx=1(6)fy=2(7)gx=4x(8)gy=2y

Just show code and the result

def f(x, y):
    return x + 2*y - 3
def g(x, y):
    return 2*x**2 + y**2 - 5
def f1(x, y):
    return 1
def f2(x, y):
    return 2
def g1(x, y):
    return 4*x
def g2(x, y):
    return 2*y
def newton(x0, y0, eps,is_print=True):
    x = x0
    y = y0
    while abs(f(x, y)) > eps or abs(g(x, y)) > eps:
        x = x - (f(x, y)*g2(x, y) - g(x, y)*f2(x, y))/(f1(x, y)*g2(x, y) - f2(x, y)*g1(x, y))
        y = y - (g(x, y)*f1(x, y) - f(x, y)*g1(x, y))/(f1(x, y)*g2(x, y) - f2(x, y)*g1(x, y))
        if is_print:
            print(x, y)
    return x, y
newton(-1, 2, 1e-4)
def f(x, y):
    return x + 2*y - 3


def g(x, y):
    return 2*x**2 + y**2 - 5


def f1(x, y):
    return 1


def f2(x, y):
    return 2


def g1(x, y):
    return 4*x


def g2(x, y):
    return 2*y


def newton(x0, y0, eps, is_print=True):
    x = x0
    y = y0
    while True:
        if is_print:
            print(x, y)
            print(f1(x, y), f2(x, y), f(x, y))
            print(g1(x, y), g2(x, y), g(x, y))
        dx = - (f(x, y)*g2(x, y) - g(x, y)*f2(x, y))/(f1(x, y)*g2(x, y) - f2(x, y)*g1(x, y))
        dy = - (f1(x, y)*g(x, y) - f(x, y)*g1(x, y))/(f1(x, y)*g2(x, y) - f2(x, y)*g1(x, y))
        x = x + dx
        y = y + dy
        if is_print:
            print(dx , dy)
        if abs(dx) < eps and abs(dy) < eps:
            break
    return x, y

newton(-1, 2, 1e-4)
-1 2
1 2 0
-4 4 1
0.16666666666666666 -0.08333333333333333
-0.8333333333333334 1.9166666666666667
1 2 0.0
-3.3333333333333335 3.8333333333333335 0.06250000000000089
0.011904761904762074 -0.005952380952381037
-0.8214285714285713 1.9107142857142858
1 2 4.440892098500626e-16
-3.285714285714285 3.8214285714285716 0.00031887755102033566
6.136475208622433e-05 -3.068237604333421e-05





(-0.8213672066764851, 1.9106836033382424)

改进的牛顿迭代法 -- 牛顿下山法

依然,我们想求解方程f(x)=0,其中f(x)是一个可导函数。我们的目标是找到一个x0,使得f(x0)=0即可;

由于传统的牛顿迭代法可能迭代到某个点之后就发散了,所以我们牛顿迭代法的基础上,引入了一个步长因子λ,使得迭代公式变为

(9)xn+1=xnλf(xn)f(xn)

这样,我们就可以在迭代过程中,通过调整步长因子λ,来控制迭代的速度,从而达到更好的收敛效果。调节步长因子λ的方法有很多,这里我们采用的是二分法。

def f(x):
    return x**2 - 2
def f1(x):
    return 2*x
def newton_downhill(x0, eps, is_print=True):
    x = x0
    while True:
        l = 1.0
        while abs(f(x - l*f(x)/f1(x))) >= abs(f(x)):
            l /= 2
        x = x - l*f(x)/f1(x)
        if abs(- l*f(x)/f1(x)) < eps:
            break
        if is_print:
            print(x)
    return x
newton2(0.5, 1e-4)
def f(x):
    return x**2 - 2


def f1(x):
    return 2*x


def newton_downhill(x0, eps, is_print=True):
    x = x0
    while True:
        l = 1.0
        while abs(f(x - l*f(x)/f1(x))) >= abs(f(x)):
            l /= 2
        x = x - l*f(x)/f1(x)
        if is_print:
            print(x)
            print(abs(- l*f(x)/f1(x)))
        if abs(- l*f(x)/f1(x)) < eps:
            break
    return x

newton_downhill(0.5, 1e-4)
1.375
0.019886363636363636
1.4147727272727273
0.0005590543994158503
1.4142136728733115
1.1050021215020399e-07





1.4142136728733115
f(0.5)
-1.75
f(2.25)
3.0625
f(1.375)
-0.109375
# 假如用传统方法
def newton(x0, eps,isprint=True):
    x = x0
    while True:
        x = x - f(x)/f1(x)
        if isprint:
            print(x)
            print(abs(f(x)/f1(x)))
        if abs(f(x)/f1(x)) < eps:
            break
    return x
newton(0.5, 1e-4)
2.25
0.6805555555555556
1.5694444444444444
0.14755408062930186
1.4218903638151426
0.007656077875069233
1.4142342859400734
2.072341514131297e-05





1.4142342859400734

小结

这个例子中,用牛顿下山法去做,我们可以看到,迭代的过程中,步长因子λ是在不断减小的,这样,我们就可以在迭代过程中,通过调整步长因子λ,来控制迭代的速度,从而达到更好的收敛效果。

但是用普通牛顿迭代法达到同样的精度也只是多一次迭代而已,只能说这个例子可能不是很好,但牛顿下山法确实一定程度上可以防止迭代发散的情况。

posted @   Link_kingdom  阅读(2467)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示