牛顿法与方程近似根

· · 算法·理论

引入

一般来说,求高次方程的精确根十分困难,而四次以上的方程甚至根本不存在求根公式。因此,求出高次方程的近似根便极为重要。

前置芝士

1.导数(derivative)

如何求一个二次函数 y=x^2 在某一点 P_0(x_0,y_0) 处的切线 P_0T 的斜率 k_0

如果用初中生的思维,那你的做法一定是这样:

设切线 P_0T:y=k_0x+b,则:

y=x^2&\\ y=k_0x+b&\\ \end{cases}

由上式减下式得:

x^2-k_0x-b=0

由判别式得:

\Delta=b^2-4ac=k_0^2+4b=0

从而:

k_0^2=-4b

将点 P_0(x_0,y_0) 代入 y=k_0x+b 得:

k_0x_0+b=y_0 b=y_0-x_0k_0

b=y_0-x_0k_0 代入 k_0^2=-4b 得:

k_0^2=-4(y_0-x_0k_0)=4x_0k_0-4y_0 k_0^2-4x_0k_0+4y_0=0

最后用求根公式:

k_0=\dfrac{-b\pm\sqrt{b^2-4ac}}{2a}=\dfrac{4x_0\pm\sqrt{16x_0^2-16y_0}}{2}

y_0=x_0^2 代入得:

k_0=\dfrac{4x_0\pm\sqrt{0}}{2}=2x_0 \therefore$ 点 $P_0$ 处的切线 $P_0T$ 的斜率 $k_0=2x_0\, .

这是初中生的做法,那什么是高中生的做法呢?

如下:

f(x)=x^2,则:

f'(x_0) &= \lim\limits_{\Delta x\to0}\dfrac{f(x_0+\Delta x)-f(x_0)}{\Delta x}=\lim\limits_{\Delta x\to0}\dfrac{(x_0+\Delta x)^2-x_0^2}{\Delta x} \\ &= \lim\limits_{\Delta x\to0}\dfrac{x_0^2+2x_0\cdot\Delta x+(\Delta x)^2-x_0^2}{\Delta x} \\ &= \lim\limits_{\Delta x\to0}\dfrac{2x_0\cdot\Delta x+(\Delta x)^2}{\Delta x} \\ &= \lim\limits_{\Delta x\to0}(2x_0+\Delta x) \\ &= 2x_0 \end{aligned} \therefore$ 点 $P_0$ 处的切线 $P_0T$ 的斜率 $k_0=2x_0\, .

与前面的一比,是不是明显简洁多了?而这种做法,就是我所说的导数

百度百科对导数的定义如下:

设函数 y=f(x) 在点 x_0 的某个邻域内有定义,当自变量 xx_0 处有增量 \Delta x(x_0+\Delta x) 也在该邻域内时,相应地函数取得增量 \Delta y=f(x_0+\Delta x)-f(x_0);如果 \Delta y\Delta x 之比当 \Delta x\to0 时极限存在,则称函数 y=f(x) 在点 x_0 处可导,并称这个极限为函数 y=f(x) 在点 x_0 处的导数,记作 f'(x_0)y'\vert _{x=x_0}\dfrac{\operatorname{d}\!y}{\operatorname{d}\!x}\vert _{x=x_0}\, .

另外,如果函数 f(x) 在点 x_0 处存在导数简称函数 f(x) 在点 x_0可导,否则不可导。若函数 f(x) 在区间 (a,b) 内每一点都可导,就称函数 f(x) 在区间 (a,b) 内可导。

来自小白的惶恐 简单地说,就是表示函数曲线在点 P_0(x_0,f(x_0)) 处的切线的斜率。对,就是我们刚刚求的那个。

随堂练习:求函数 f(x)=x^4-\dfrac{2\ln x}{x^3}+\ln(2x^2)-xx=e 处的导数 \cdots\cdots 开个玩笑,求函数 g(x)=\sqrt{x}x=3 处的导数。

算好了吗?现在公布答案:

g'(3) &= \lim\limits_{\Delta x\to0}\dfrac{\sqrt{3+\Delta x}-\sqrt{3}}{\Delta x} \\ &= \lim\limits_{\Delta x\to0}\dfrac{(\sqrt{3+\Delta x}-\sqrt{3})(\sqrt{3+\Delta x}+\sqrt{3})}{\Delta x(\sqrt{3+\Delta x}+\sqrt{3})} \\ &= \lim\limits_{\Delta x\to0}\dfrac{\Delta x}{\Delta x(\sqrt{3+\Delta x}+\sqrt{3})} \\ &= \lim\limits_{\Delta x\to0}\dfrac{1}{\sqrt{3+\Delta x}+\sqrt{3}} \\ &= \dfrac{1}{2\sqrt{3}}\,. \end{aligned}

开玩笑的那个有同学算了吗?公布答案:

f'(x) &=4x^3- \left[ \dfrac{\dfrac{2}{x}\cdot x^3-3x^2\cdot2\ln x}{(x^3)^2} \right] +\dfrac{2}{x}-1 \\ &= 4x^3-\dfrac{2-6\ln x}{x^4}+\dfrac{2}{x}-1\,, \end{aligned} \therefore f'(e) &= 4e^3-\dfrac{2-6\ln e}{e^4}+\dfrac{2}{e}-1 \\ &= 4e^3+\dfrac{4}{e^4}+\dfrac{2}{e}-1\,. \end{aligned}

或许有同学会说:诶,等等,这怎么算的啊。那,是时候把导数的运算法则教给你了。

  1. \left[f(x)\pm g(x)\right]'=f'(x)\pm g'(x)\,;
  2. \left[f(x)g(x)\right]'=f'(x)g(x)+f(x)g'(x)\,;
  3. \left[\dfrac{f(x)}{g(x)}\right]'=\dfrac{f'(x)g(x)-f(x)g'(x)}{\left[g(x)\right]^2}\quad(g(x)\ne0)\,.

什么?难道我们问的真的不是一个函数的导数怎么算么? 当然,还有一些基本初等函数的导数公式,它们都可以直接使用:

  1. f(x)=c\,\,(c 为常数 ),则 f'(x)=0\,;

  2. f(x)=x^\alpha\,\,(\alpha\in\mathbb{Q} ,且 \alpha\ne0\,),则 f'(x)=\alpha x^{\alpha-1}\,;

  3. f(x)=\sin x,则 f'(x)=\cos x\,;

  4. f(x)=\cos x,则 f'(x)=-\sin x\,;

  5. f(x)=a^x\,\,(a>0 ,且 a\ne1\,),则 f'(x)=a^x\ln a\,; 特别地,若 f(x)=e^x,则 f'(x)=e^x\,;

  6. f(x)=\log_a x\,\,(a>0,且 a\ne1\,),则 f'(x)=\dfrac{1}{x\ln a}\,; 特别地,若 f(x)=\ln x,则 f'(x)=\dfrac{1}{x}\,.

2.高阶导数(higher derivative)

先举个简单的栗子:求函数 f(x)=x^2 的二阶导数。

我们已经知道 f(x) 的一阶导数是 f'(x)=2x,我们只要再对这个一阶导数求导即可,即 f''(x)=\left[f'(x)\right]'=2\,.

定义:

函数 y=f(x) 的导数 y'=f'(x) 仍是 x 的函数。我们把 y'=f'(x) 的导数叫做函数 y=f(x)二阶导数(second derivative),记作 y''\dfrac{\operatorname{d}\!^2y}{\operatorname{d}\!x^2},即 y''=(y')'\dfrac{\operatorname{d}\!^2y}{\operatorname{d}\!x^2}=\dfrac{\operatorname{d}}{\operatorname{d}\!x}\left(\dfrac{\operatorname{d}\!y}{\operatorname{d}\!x}\right)。以此类推,n-1 阶导数的导数叫做 n 阶导数(nth derivative),记作:

y',y'',y''',y^{(4)},\dots,y^{(n)},\dots

\dfrac{\operatorname{d}\!y}{\operatorname{d}\!x},\dfrac{\operatorname{d}\!^2y}{\operatorname{d}\!x^2},\dfrac{\operatorname{d}\!^3y}{\operatorname{d}\!x^3},\dfrac{\operatorname{d}\!^4y}{\operatorname{d}\!x^4},\dots,\dfrac{\operatorname{d}\!^n y}{\operatorname{d}\!x^n},\dots

3.泰勒级数(Taylor series)

百度百科:在数学中,泰勒级数用无限项连加式——级数来表示一个函数,这些相加的项由函数在某一点的导数求得。

定义:如果在点 x=x_0 具有任意阶导数,则幂级数

\sum^\infty_{n=0}\dfrac{f^{(n)}(x_0)}{n!}(x-x_0)^n=f(x_0)+f'(x_0)(x-x_0)+\dfrac{f''(x_0)}{2!}(x-x_0)^2+\cdots+\dfrac{f^{(n)}(x_0)}{n!}+\cdots

称为在点 x_0 处的泰勒级数

我们可以用 R_n(x) 表示泰勒公式中的余项,即:

\sum^\infty_{n=0}\dfrac{f^{(n)}(x_0)}{n!}(x-x_0)^n &= f(x_0)+f'(x_0)(x-x_0)+\dfrac{f''(x_0)}{2!}(x-x_0)^2+\cdots+\dfrac{f^{(n)}(x_0)}{n!}+\cdots \\ &= f(x_0)+f'(x_0)(x-x_0)+\dfrac{f''(x_0)}{2!}(x-x_0)^2+\cdots+\dfrac{f^{(n)}(x_0)}{n!}+R_n(x) \end{aligned}

4.直线(straight line)

难的学完了再看看简单的:已知直线 y=kx+b,求它与坐标轴的交点。

嗯,有手就行:与 x 轴交点 (-\dfrac{b}{k},0),与 y 轴交点 (0,b)

那这个呢?已知直线 l 斜率为 k,且过点 (x_0,y_0),求直线 l 的直线解析式。

很简单,运用一点初中知识,我们得到 l:y=kx+y_0-kx_0,那么它与 x 轴的交点的横坐标就是 \dfrac{kx_0-y_0}{k}\,.

正题

牛顿迭代法(Newton's method)

大前提:函数 f(x) 可导!

百度百科的定义:

rf(x)=0 的根,选取 x_0 作为 r 的初始近似值,过点 (x_0,f(x_0)) 做曲线 y=f(x) 的切线 LL:y=f(x_0)+f'(x_0)(x-x_0),则 Lx 轴交点的横坐标 x_1=x_0-\dfrac{f(x_0)}{f'(x_0)},称 x_1r 的一次近似值。过点 (x_1,f(x_1)) 做曲线 y=f(x) 的切线,并求该切线与 x 轴交点的横坐标 x_2=x_1-\dfrac{f(x_1)}{f'(x_1)},称 x_2r 的二次近似值。重复以上过程,得 r 的近似值序列,其中,x_{n+1}=x_n-\dfrac{f(x_n)}{f'(x_n)} 称为 rn+1 次近似值,上式称为牛顿迭代公式。

那为什么它可以成立呢?这要涉及到泰勒级数。用牛顿迭代法解非线性方程,是把非线性方程 f(x)=0 线性化的一种近似方法。把 f(x)=0 在点 x_0 的某邻域内展开成泰勒级数 f(x_0)=f(x_0)+f'(x_0)(x-x_0)+\dfrac{f''(x_0)}{2!}(x-x_0)^2+\cdots+\dfrac{f^{(n)}(x_0)}{n!}+R_n(x),取其线性部分(即泰勒展开的前两项),并令其等于 0,即 f(x_0)+f'(x_0)(x-x_0)=0,以此作为非线性方程 f(x)=0 的近似方程,若 f'(x_0)\ne0,则其解为 x_1=x_0-\dfrac{f(x_0)}{f'(x_0)},这样,得到牛顿迭代法的一个迭代关系式:x_{n+1}=x_n-\dfrac{f(x_n)}{f'(x_n)}

这么长一堆,真是可怕!但我们只需按照它的步骤,一步步来,我们也能求出近似根。

好,那么开始了?如果你觉得你的运算能力不错,可以心算,但有计算器的话。嘿嘿,那就 不讲武德! 拿出来用吧。

方程:x^3+2=0,初始近似值为 x_0=-2\,.

计算器的步骤:x,-,\dfrac{\fcolorbox{black}{black}{}}{\Box},x,\text{shift},x^2\,(x^3),+,2,,\text{shift},\int^\Box_\Box\fcolorbox{black}{black}{ }\,(\dfrac{\operatorname{d}}{\operatorname{d}\!x}\fcolorbox{black}{black}{}),(,x,\text{shift},x^2\,(x^3),+,2,),,x,,\text{sto},x\,.在这之前先设置 x2,然后一直按 = 键就行了。

算好了吗?当然算不好,我还没告诉你们精确度呢,真是悲催。

所谓精确度,用希腊字母 \varepsilon 表示,当 \lvert x_{i+1}-x_i\rvert<\varepsilon 时,算法就可以停止,否则就继续算下去。

好,那现在给定精确度 \varepsilon=10^{-6},算出来近似根是多少?

嗯~,应该是 -1.25992\cdots 吧。再开个三次方,你就会发现它确实很接近 -2,这说明牛顿迭代法是有效的不是吗?

但是你再试试这个:\sqrt[3]{x}=0,初始近似值为 x_0=-2,精确度 \varepsilon=10^{-6}\,.

啊呀呀,这下好了,不管你是用计算器还是心算,你都会发现近似根的绝对值在不断增大,可方程的根一看就是 x=0 啊。(如图)

来源:几何画板。

是时候 展现真正的技术了! 对牛顿迭代法进行改进了!

牛顿下山法(Newton down-hill method)

要想练就绝世武功,就要忍受常人难忍受的痛

百度百科,稍微看看就行了。

大体意思即:为了保证 x_k 是在向根 r 逼近,对牛顿迭代法提出 \lvert f(x_{k+1})\rvert<\lvert f(x_k)\rvert 的条件(即函数值必须是在逼近 0),并有了新的迭代关系式:x_{k+1}=x_k-\lambda_k\dfrac{f(x_k)}{f'(x_k)}\,.

其中 \lambda_k 称为“下山因子”,满足 0<\lambda_k\le 1。当 \lambda_k 取某个值时,如果仍有 \lvert f(x_{k+1})\rvert\ge\lvert f(x_k)\rvert,就对 \lambda_k 进行减半操作,直到满足条件为止。

可参考:https://wenku.baidu.com/view/8d98b85ff78a6529647d53bd.html?fr=sogou&_wkts_=1668844957938

可以写出整数次幂非线性方程求解的代码,如下:

#include<cmath>
#include<iomanip>
#include<iostream>
using namespace std;
int _f[110],_f_[110],n;
double r,r_pre,epsilon;
double f(double x){
    double sum=_f[n];
    for(int i=n-1;i>=0;i--) sum*=x,sum+=_f[i]; //秦九韶算法 
    return sum;
}
double f_(double x){
    double sum=_f_[n-1];
    for(int i=n-2;i>=0;i--) sum*=x,sum+=_f_[i];
    return sum;
}
int main(){
    while(n<2) cin>>n; //保证是非线性方程 
    for(int i=n;i>0;i--){
        cout<<"请输入"<<i<<"次项:";
        cin>>_f[i];
        _f_[i-1]=_f[i]*i;
    }
    cout<<"请输入常数项:";
    cin>>_f[0];
    cout<<"请输入精确度:";
    cin>>epsilon;
    cout<<"请输入初始近似根:";
    cin>>r;
    do{
        r_pre=r;
        for(double lambda=1;;lambda/=2){ //牛顿下山法 
            r=r_pre-lambda*f(r_pre)/f_(r_pre);
            if(fabs(f(r))<fabs(f(r_pre))) break;
        }
    }while(fabs(r_pre-r)>=epsilon);
    cout<<fixed<<setprecision((int)log10(1/epsilon))<<r<<endl; //有效位数 
    cout<<fixed<<setprecision((int)log10(1/epsilon))<<f(r);
    return 0;
}

它甚至能解一百次方程! 就我试验来看,它是没问题的。

后置芝士

1.复数(complex number)

上过初中的同学应该知道,方程 x^2+1=0\Delta 小于 0,是没有实数根的。诶,好奇怪,为什么不说无解呢?那是因为它是有解的(这不是废话吗),只不过涉及到 \sqrt{-1} 的问题罢了。

我们令 i^2=-1,并将形如 z=a+bia,b 均为实数)的数称为复数,其中 a 称为实部 (real part),b 称为虚部(imaginary part),i 称为虚数单位(imaginary unit)。当 b=0 时,z 为实数;当 a=0b\ne 0 时,z纯虚数(pure imaginary)。

这样就好了,方程 x^2+1=0 虽然在实数域没有根,但在复数域就有两个根 x_1=i,x_2=-i\,.

复数的运算法则:

  1. (a+bi)\pm(c+di)=(a\pm c)+(b\pm d)i\,;
  2. (a+bi)(c+di)=(ac-bd)+(bc+ad)i\,;
  3. \dfrac{a+bi}{c+di}=\dfrac{(a+bi)(c-di)}{(c+di)(c-di)}=\dfrac{(ac+bd)+(bc-ad)i}{c^2+d^2}\,.

零碎知识点:

2.复数平面(complex plane)

百度百科:复数平面即是 z=a+bi,它对应的坐标为 (a,b)。其中,a 表示的是复平面内的横坐标,b 表示的是复平面内的纵坐标,表示实数 a 的点都在 x 轴上,所以 x 轴又称为“实轴”;表示纯虚数 bi 的点都在 y 轴上,所以 y 轴又称为“虚轴”。y 轴上有且仅有一个实点即为原点"0"。

复平面内的每一个点,有唯一的一个复数和它对应;反过来,每一个复数,有复平面内唯一的一个点和它对应,所以复数集 \mathbb{C} 和复平面内所有的点所成的集合是一一对应的。

3.代数基本定理(fundamental theorem of algebra)

百度百科:任何复系数一元 n\,\,(n\ge1) 次多项式方程在复数域上至少有一根。

牛顿法与复数根

同实数根一样,只需将平面直角坐标系换成复平面,就有递推式 z_{k+1}=z_k-\dfrac{f(z_k)}{f'(z_k)},同样也可以得到复数近似根。

闭幕

文中引用资料,侵权删。