数值分析基础求单根算法

函数f(x)=x^3-x-1在[1,2]上有单根,要求精度在10^-5。

二分法:


public class Half {
    public static double fx(double x) {
        return x*x*x-x-1;
    }
    public static void main(String []args) {
        int count=0;
        double precesion=0.00001;
        double a=1,b=2;
        double half=0;
        while(fx(a)*fx(b)<0) {//当a,b区间有根时
            half=(a+b)/2.0;
            if(fx(half)>0) {//不断将区间分为原来的一半
                b=half;
            } else {
                a=half;
            }
            count++;//迭代次数 ++
            if(fx(b)-fx(a)<=precesion) {//如果解的精度达到要求
                break;
            }
        }
        System.out.println(half+"  "+count);

    }
}

二分法是一个比较简单易懂的方法,就不详细说了。

迭代法:


public class Iteration {
    //迭代法 最主要的是构造一个 收敛的 迭代函数 如这题
    //x=φ(x)=(x+1)^(1/3)开三次方,是收敛的,如果换成别的,像
    //x=φ(x)=x^3-1  是发散的,找不到结果的
    public static double φ(double x) {
        return Math.pow(x+1,1/3.0);
    }

    public static void main(String []args) {
        int count=0;
        double x0=1,x1=0;
        double precision=0.00001;
        while(true) {
            count++;
            x1=φ(x0);
            if(Math.abs(x1-x0)<=precision) {
                break;
            }
            x0=x1;
        }

        System.out.println(x1+" "+count);
    }

}

迭代法是由x1作为迭代函数的值,x0作为参数,一轮迭代后比较二者差值与精度,不够再将x1赋给x0,作为下一轮迭代。。。。对于函数由本来的f(x)=0,变为x=φ(x) 迭代函数,进行一个压缩变换,是有解的关键。

牛顿迭代法:
有的迭代法虽然可以求解,但是迭代速度太慢,打不到要求,于是更加快速的Newton迭代法出炉了


public class NewtonIteration {

    private final static double precesion=0.00001;

    public static double Derivative(double x0) {
        //用 △fx/△x 精度为0.00001  做   导数
        return (f(x0+precesion)-f(x0))/precesion;
    }//导数我是求出它的近似解

    public static double f(double x) {
        return x*x*x-x-1;
    }
    public static void main(String[]args) {
        double x0=1;
        double x1=0;
        int count=0;
        while(Derivative(x0)!=0) {
            x1=x0-f(x0)/Derivative(x0);

            if(Math.abs(x1-x0)<=precesion) {
                break;
            }
            count++;
            x0=x1;

        }
        System.out.println(x1+" "+count);

    }


}

只要理解了上面那副图就很容易写出代码。但是这个方法很依赖于初值的选择,只要初值选不好,就很容易导致最后结果发散,形成死循环

弦截法:
避免了牛顿法的求导问题,用曲线上两点之间形成的直线与x轴交点做下一个x

这里写图片描述

两点之间直线的公式 ax+by+c=0,只要确定a,b,c参数就可以做了

public class SecantMethod {

    public static double f(double x) {
        return x*x*x-x-1;
    }

    public static void main(String[]args) {
        double a=1,b=2;
        double precesion=0.00001;
        int count=0;
        System.out.println("弦截法");
        while(true) {
            a=a-f(a)*(b-a)/(f(b)-f(a));//弦与 x轴的交点 
            if(Math.abs(f(a))<=precesion) {
                break;
            }
            count++;

        }

        System.out.println(a+" "+count);
    }

}

还有对迭代方向有优化的牛顿下山法,Aitken算法:
Aitken算法是利用两次暂时的迭代减小导数对计算精度的影响,并缩小每次迭代的误差。

package find_root;

public class Aitken {
    //和牛顿下山法  差不多  都是对每一轮 近似的根 进行补偿
    //但是  这里可以避免导数的误差对 计算精度的影响

    public static double f(double x) {//迭代函数x=e^-x
        return Math.exp(-x);
    }
    public static void calculation() {
        double x0=0.5,x1=0,xt1=0,xt2=0;
        int count=0;
        while(true) {
            xt1=f(x0);//第一个暂时变量
            xt2=f(xt1);//第二个
            x1=xt2-(xt2-xt1)*(xt2-xt1)/(xt2-2*xt1+x0);//用两个暂时变量
            //的线性组合 , 减小近似点与解的差距,提高迭代速度
            count++;
            if(Math.abs(x1-x0)<0.00001) {
                break;
            }
            x0=x1;

        }
        System.out.println(x1+" "+count);

    }
    public static void main(String[]args) {
        calculation();

    }

}

牛顿下山法:由于牛顿迭代法对于初值的选择比较重要,限制了他的应用,于是就增加一个常数C xk+1=xk-C*f(xk)/f’(xk) 使得 |f(k+1)|<|f(k)|,每一轮的C都可能不同,一般C=1,然后一个循环,找到合适的C,再参与迭代。

package find_root;

public class Downhill_method {

    private final static double precesion=0.000001;

    public static double derivative(double x0) {
    //用 △fx/△x 精度为0.00001  做   导数
        return (f(x0+precesion)-f(x0))/precesion;
    }

    public static double f(double x) {
        return x*x*x-x-1;//迭代函数  x=x^3-x-1
    }

    public static void calculation() {
        double x0=1.5,x1=0;
        int count=0;
        while(true) {
            for(double C=1;;C=C*1.0/2){//下山因子  防止迭代发散
                x1=x0-C*f(x0)/derivative(x0);
                if(Math.abs(f(x1)-f(x0))<precesion) {
                    break;
                }
            }
            count++;
            if(Math.abs(x1-x0)<precesion) {
                break;
            }
            x0=x1;  
        }
        System.out.println(x1+"  "+count);

    }

    public static void main(String[]args) {
        calculation();
    }
}

如有需要详细的数学证明的,可以查阅由金聪,熊盛武编写的《数值分析》。

猜你喜欢

转载自blog.csdn.net/qq_36254754/article/details/79678594
今日推荐