非线性方程求根数值解法
实验目的
(1)通过对二分法与牛顿迭代法做编程练习和上机运算,进一步体会二分法和牛顿法的不同。
(2)编写割线迭代法的程序,求非线性方程的解,并于牛顿迭代法作比较。

一、实验内容
1、用牛顿迭代法求下列方程的根
(1) x^2-e^x=0
(2) xe^x-1=0
(3) lgx+x-2=0
2、二分法解以上问题
3、编写割线法程序求解第一问的方程
实验步骤、程序设计、实验结果及分析

一、用牛顿迭代法求下列方程的根:
1.1实验步骤:
以第一题为例,列举实验步骤
令 f(x)= x^2-e^x
求得f’(x)=2×x -e^x
xk+1=xk-f(x)/f’(x)
迭代计算即可
1.2程序设计:
(1) x^2-e^x=0

#include <bits/stdc++.h>
using namespace std;
int main()
{
    double x0,x;
    x=1;
    do
    {
        x0=x;
        double fx=x*x-exp(x);
        double fx2=x*2-exp(x);
        x=x0-(fx/fx2);
    }while(fabs(x-x0)>=1e-5);
    cout<<x0<<endl;
    return 0;
}

(2) xe^x-1=0

#include <bits/stdc++.h>
using namespace std;
int main()
{
    double x0,x;
    x=1;
    do
    {
        x0=x;
        double fx=x*exp(x)-1;
        double fx2=x*exp(x)+exp(x);
        x=x0-(fx/fx2);
    }while(fabs(x-x0)>=1e-5);
    cout<<x0<<endl;
    return 0;
}

分析:
通过牛顿迭代法,求出f(x)的导数,根据xk+1=xk-f(x)/f’(x)
进行迭代,通过实验发现我们只需要通过5次迭代就可以求出答案

(3) lgx+x-2=0

#include <bits/stdc++.h>
using namespace std;
int main()
{
    double x0,x;
    x=1;
    do
    {
        x0=x;
        double fx=log10(x)+x-2;
        double fx2=1/x+1;
        x=x0-(fx/fx2);
    }while(fabs(x-x0)>=1e-5);
    cout<<x0<<endl;
    return 0;
}

分析:
通过牛顿迭代法,求出f(x)的导数,根据xk+1=xk-f(x)/f’(x)
进行迭代,通过实验发现我们只需要通过9次迭代就可以求出答案
二、二分法求方程的根
2.1实验步骤
1)我们给定一个初始的范围,这个范围要求在区间内函数单调并且有且仅有一个根
2)通过判断 mid=(r+l)/2 是不是满足f(mid)=0的条件,如果不满足就缩小范围(l,mid) 或者是(mid,r)
3)当l,r的区间长度小于一个很小的数eps的时候,那么我们就相当于求出了方程的根
2.2程序设计
(1) x^2-e^x=0

#include<bits/stdc++.h>
#define eps 1e-5
using namespace std;

bool check(double k)
{
    if(k*k-exp(k)>1e-4)return true;
    else return false;
}
int main()
{
    double l=-1,r=0;
    int cnt=0;
    while(r-l>eps)
    {
        double mid=l+(r-l)/2;
        //cout<<mid<<endl;
        cnt++;
        if(check(mid))
        {
            l=mid;
        }
        else r=mid;
    }
printf("cnt=%d\n",cnt);
printf("answer=%.5lf\n",r);
    return 0;

}

分析:二分法的迭代次数,显然与要求的精度和其实两数的范围有关
2)xe^x-1=0

#include<bits/stdc++.h>
#define eps 1e-4
using namespace std;

bool check(double k)
{
    if(k*exp(k)-1>1e-4)return true;
    else return false;
}
int main()
{
    double l=-1,r=1;
    int cnt=0;
    while(r-l>eps)
    {
        double mid=l+(r-l)/2;
        //cout<<mid<<endl;
        cnt++;
        if(check(mid))
        {
            r=mid;
        }
        else l=mid;
    }
printf("cnt=%d\n",cnt);
printf("answer=%.7lf\n",r);
    return 0;

}

3)lgx+x-2=0

#include<bits/stdc++.h>
#define eps 1e-4
using namespace std;

bool check(double k)
{
    if(log10(k)+k-2>1e-4)return true;
    else return false;
}
int main()
{
    double l=-2,r=2;
    int cnt=0;
    while(r-l>eps)
    {
        double mid=l+(r-l)/2;
        //cout<<mid<<endl;
        cnt++;
        if(check(mid))
        {
            r=mid;
        }
        else l=mid;
    }
printf("cnt=%d\n",cnt);
printf("answer=%.7lf\n",r);
    return 0;

}

三、割线法求第一问
3.1实验步骤
1)令 f(x)= x^2-e^x
2)x_(k+1)=x_k-f(x_k )/((f(x_k )-〖f(x〗(k-1) ) )*(x_k-x(k-1))
3)迭代计算即可
3.2程序设计

#include <bits/stdc++.h>
using namespace std;
int main()
{
    double xk,x1,xk2;
    xk2=1;
    xk=-1;
    do
    {
        x1=xk2;
        double fx=xk*xk-exp(xk);
        double fx2=xk2*xk2-exp(xk2);
        xk2=xk2-(fx2/(fx2-fx))*(xk2-xk);
        xk=x1;
    }while(fabs(xk2-xk)>=1e-5);
    cout<<xk<<endl;
    return 0;
}

使用割线法求解,根据公式x_(k+1)=x_k-f(x_k )/((f(x_k )-〖f(x〗(k-1) ) )*(x_k-x(k-1)),迭代次数与初始的设定值有关,当初始值设定为1和-1时,我们需要迭代7次,当初始值设为-0.5和-1时,迭代6次,因此我们可以知道,两个初始值尽可能在方程的解旁时,能有效的减少迭代次数。

总结

牛顿迭代法具有收敛速度快,能求重根等有点,但是每迭代一步,都要计算函数的导数值,计算量很大,尤其是当函数的结构比较复杂或者函数不可导的时候,就很难使用牛顿迭代法,为了克服这种缺点,常常采用离散牛顿法,与牛顿法相比,它避免了求函数f(x)的导数,但需要两个初始值,且这两个初始值尽量取在方程的根的附近,其收敛速度一般比牛顿法慢。但比现行收敛要快。而二分法通过不断二分缩小区间,二分的迭代次数与要求的精度和设置的起始数据有关。