Java 求函数根的割线法的实现

Java 求函数根的割线法的实现,java,function,math,methods,Java,Function,Math,Methods,我编写了以下代码作为割线方法的实现。它恰巧是在java中,但基本上可以与C++等类似语言(和C,如果忽略异常)进行可译。 在这段代码中,(f)是实现函数接口的类,(a)是根的先前近似值,(b)是根的当前近似值,(epsilon)据我所知,是要完成的方法的两个最新近似值之间的最大差异。如果我相信这一点是正确的,那么检查| c-b |是否大于(epsilon)会起作用吗 这对于正常函数(如x^2+2x+1)似乎很好,但当我尝试运行find时,出现了一个问题,函数返回Math.log(x)——它找到的

我编写了以下代码作为割线方法的实现。它恰巧是在java中,但基本上可以与C++等类似语言(和C,如果忽略异常)进行可译。 在这段代码中,(f)是实现函数接口的类,(a)是根的先前近似值,(b)是根的当前近似值,(epsilon)据我所知,是要完成的方法的两个最新近似值之间的最大差异。如果我相信这一点是正确的,那么检查| c-b |是否大于(epsilon)会起作用吗

这对于正常函数(如x^2+2x+1)似乎很好,但当我尝试运行find时,出现了一个问题,函数返回Math.log(x)——它找到的根是NaN


我的代码是否有问题,或者我只是没有正确理解实际的数学?如果有人能解释一下,我将不胜感激。

割线法不能保证收敛。例如,从方法中获得的点c可能恰好位于函数的域之外。当f处处定义(例如多项式)时,这不是问题,但对数仅为参数的正值定义:

当正割线与x轴的交点位于负半部分时,由于试图取负数的对数,您最终调用
find(f,b,c,epsilon)

可能的补救办法:

  • 引发异常并告诉用户使用不同的初始值重试

  • 尝试恢复:如果f(b)未定义,则将b替换为(a+b)/2(例如,
    返回find(f,a,(a+b)/2,epsilon)
    。这可能有助于算法恢复,但另一方面,您可能会陷入无限循环(在您的情况下是递归)


  • 所述方法存在问题,可能不收敛。最简单的方法是从两个数字a和b开始,其中
    f(a)
    f(b)
    具有不同的符号。前提是f在
    [a;b]
    上是连续的(假设
    a
    )割线算法将给出
    c
    a
    。事实上,你没有保留最后的猜测,而是保留最好的猜测

    然后,如果
    f(c)
    f(b)
    具有相同的符号,则使用
    a,c
    进行迭代(或使用实现进行递归),否则使用
    c,b
    进行迭代

    可能的实施:

    public class Secant
    {
        static double find(Function f, double a, double b, double epsilon)
        {
            double temp;
            if (b < a) {
                 temp = a;
                 a = b;
                 b = temp;
            }
            double fa = f.f(a), fb = f.f(b);
    
            if (fa == fb) {
                throw new IllegalArgumentException();
            }
    
            double c = b - ((fb * (b - a)) / (fb - fa));
    
            if (Math.abs(c - b) > epsilon) {
                if (f.f(a) * f.f(b) < 0) { // different signs for f(a) and f(b)
                    if (f.f(a) * f.f(c) < 0) {
                        return find(f, a, c, epsilon);
                    }
                    else {
                        return find(f, c, b, epsilon);
                    }
                }
                else { // same signs for f(a) and f(b)
                    if (((f.f(a) < f.f(b)) && (f.f(c) < f.f(a))) || ((f.f(a) > f.f(b)) && (f.f(c) > f.f(a)))) {
                        // f(a) between f(b) and f(c)
                        return find(f, a, c, epsilon);
                    }
                    else {
                        return find(f, c, b, epsilon);
                    }
                }
            }
            else {
                return c;
            }
        }
    };
    
    公共类割线
    {
    静态双查找(函数f,双a,双b,双ε)
    {
    双温;
    if(bε){
    如果(f.f(a)*f.f(b)<0{//f(a)和f(b)的符号不同
    若(f.f(a)*f.f(c)<0){
    返回find(f,a,c,epsilon);
    }
    否则{
    返回查找(f,c,b,epsilon);
    }
    }
    else{//表示f(a)和f(b)的符号相同
    如果((f.f(a)f.f(b))&&&(f.f(c)>f.f(a))){
    //f(a)在f(b)和f(c)之间
    返回find(f,a,c,epsilon);
    }
    否则{
    返回查找(f,c,b,epsilon);
    }
    }
    }
    否则{
    返回c;
    }
    }
    };
    
    对于连续函数,只要找到2个数(在一个递归级别上)且其
    f(a)*f(b)<0

    如果你不能很容易地得到2个
    f(a)*f(b)<0
    ,那么只有当你已经在两个点的根和切线附近时,才应该使用这种方法,正割和函数本身都足够接近。事实上(除了它不需要任何导数函数的知识)只能在可以使用Euler方法的情况下使用。因此,正常使用情况为:

    • 首先使用二分法寻找根的邻域,因为它是一种稳健的方法,即使收敛速度很慢
    • 接下来,使用欧拉或割线方法快速获得根值的精度,因为它们收敛更快,但鲁棒性最差
    /**确定多项式根的割线法*/
    导入java.util.*;
    公共类割线{
    专用静态扫描仪输入=新扫描仪(System.in);
    私有静态列表系数=新的ArrayList();
    /**
    *输入函数的顺序和系数。如果系数不正确
    *如果存在,请输入0。
    */
    私有静态void inputFunction(){
    System.out.print(“输入多项式的顺序:”);
    int order=input.nextInt();
    System.out.print(“输入函数项的系数:”);
    而(订单>=0){
    double value=input.nextDouble();
    系数。加(值);
    命令--;
    }
    }
    /**
    *计算给定变量x的函数值的函数方法
    * 
    *@param x
    *@输入变量x的返回结果
    */
    专用静态双功能(双x){
    双结果=0.0;
    对于(int index=0,order=coverties.size()-1;indexpublic class Secant
    {
        static double find(Function f, double a, double b, double epsilon)
        {
            double temp;
            if (b < a) {
                 temp = a;
                 a = b;
                 b = temp;
            }
            double fa = f.f(a), fb = f.f(b);
    
            if (fa == fb) {
                throw new IllegalArgumentException();
            }
    
            double c = b - ((fb * (b - a)) / (fb - fa));
    
            if (Math.abs(c - b) > epsilon) {
                if (f.f(a) * f.f(b) < 0) { // different signs for f(a) and f(b)
                    if (f.f(a) * f.f(c) < 0) {
                        return find(f, a, c, epsilon);
                    }
                    else {
                        return find(f, c, b, epsilon);
                    }
                }
                else { // same signs for f(a) and f(b)
                    if (((f.f(a) < f.f(b)) && (f.f(c) < f.f(a))) || ((f.f(a) > f.f(b)) && (f.f(c) > f.f(a)))) {
                        // f(a) between f(b) and f(c)
                        return find(f, a, c, epsilon);
                    }
                    else {
                        return find(f, c, b, epsilon);
                    }
                }
            }
            else {
                return c;
            }
        }
    };
    
    /** Secant method of determining the roots of a polynomial.  */
    
    import java.util.*;
    
    public class Secant {
    
    private static Scanner input = new Scanner(System.in);
    private static List<Double> coefficients = new ArrayList<>();
    
    /**
     * Enter the order and coefficients of the function. If coefficient doesn't
     * exist, enter 0.
     */
    private static void inputFunction() {
        System.out.print("Enter the order of the polynomial: ");
        int order = input.nextInt();
    
        System.out.print("Enter the coefficient of terms of the function: ");
    
        while (order >= 0) {
            double value = input.nextDouble();
            coefficients.add(value);
            order--;
        }
    }
    
    /**
     * The function method to compute the value of the function given variable x
     * 
     * @param x
     * @return result of inputing variable x
     */
    private static double function(double x) {
        double result = 0.0;
        for (int index = 0, order = coefficients.size()-1; index < coefficients.size(); order--, index++) {
            result += coefficients.get(index) * (Math.pow(x, order));
        }
        return result;
    }
    
    public static void main(String[] args) {
    
        inputFunction();
        
        double xn1; // This represent x_n-1
        double xn, x, precision;
    
        System.out.print("Enter the start of the interval: ");
        xn1 = input.nextDouble();
    
        System.out.print("Enter the end of the interval: ");
        xn = input.nextDouble();
    
        System.out.print("Enter the precision of the interval (e.g 0.000001): ");
        precision = input.nextDouble();
    
        long startTime = System.currentTimeMillis();
        
        if (function(xn1) * function(xn) > 0.0) {
            System.out.println("Functions result have same sign value ...");
            //return;
        }
    
        x = xn1;
        int iterations = 0;
        while (Math.abs(function(x)) > precision) {
            
            System.out.println("x" + iterations + ": " + x + " f(x"+ iterations + "): " + function(x));
            
            x = xn - (function(xn) * (xn-xn1))/(function(xn) - function(xn1));
            xn1 = xn;
            xn = x;
            iterations++;
        }
        
        long stopTime = System.currentTimeMillis();
        long timeUsed = (stopTime - startTime);
        System.out.println("The root of the equation is " +
                    Math.round(x*1000000)/1000000.0);       // Answer is rounded to 6 decimal places.
        System.out.println("Time used is " + timeUsed + " milliseconds.");
    }
    }