对一个连续函数的导数进行近似

时间:2022-09-08 22:50:16

I am looking to write a method in Java which finds a derivative for a continuous function. These are some assumptions which have been made for the method -

我想用Java写一个方法,它可以找到一个连续函数的导数。这些是对方法的一些假设

  1. The function is continuous from x = 0 to x = infinity.
  2. 函数从x = 0到x =∞是连续的。
  3. The derivative exists at every interval.
  4. 导数存在于每一个区间。
  5. A step size needs to be defined as a parameter.
  6. 步骤大小需要定义为参数。
  7. The method will find the max/min for the continuous function over a given interval [a:b].
  8. 该方法将求连续函数在给定区间内的最大值/最小值[a:b]。

As an example, the function cos(x) can be shown to have maximum or minimums at 0, pi, 2pi, 3pi, ... npi.

例如,函数cos(x)在0、2、3、…npi。

I am looking to write a method that will find all of these maximums or minimums provided a function, lowerBound, upperBound, and step size are given.

我希望编写一个方法,它可以找到所有这些最大值或最小值,并提供一个函数、下界、上界和步长。

To simplify my test code, I wrote a program for cos(x). The function I am using is very similar to cos(x) (at least graphically). Here is some Test code that I wrote -

为了简化测试代码,我为cos(x)编写了一个程序。我使用的函数非常类似于cos(x)(至少是图形化的)。这是我写的一些测试代码

public class Test {
    public static void main(String[] args){
        Function cos = new Function () 
        {
        public double f(double x) {
        return Math.cos(x);
        }
    };

        findDerivative(cos, 1, 100, 0.01);      
    }

    // Needed as a reference for the interpolation function.
    public static interface Function {
    public double f(double x);
    }

     private static int sign(double x) {
    if (x < 0.0)
            return -1;
        else if (x > 0.0)
            return 1;
        else
            return 0;
    }

     // Finds the roots of the specified function passed in with a lower bound,
    // upper bound, and step size.
    public static void findRoots(Function f, double lowerBound,
                  double upperBound, double step) {
    double x = lowerBound, next_x = x;
    double y = f.f(x), next_y = y;
    int s = sign(y), next_s = s;

    for (x = lowerBound; x <= upperBound ; x += step) {
        s = sign(y = f.f(x));
        if (s == 0) {
        System.out.println(x);
        } else if (s != next_s) {
        double dx = x - next_x;
        double dy = y - next_y;
        double cx = x - dx * (y / dy);
        System.out.println(cx);
        }
        next_x = x; next_y = y; next_s = s;
    }
    }

    public static void findDerivative(Function f, double lowerBound, double 
            upperBound, double step) {
    double x = lowerBound, next_x = x;
    double dy = (f.f(x+step) - f.f(x)) / step;

    for (x = lowerBound; x <= upperBound; x += step) {
        double dx = x - next_x;
        dy = (f.f(x+step) - f.f(x)) / step;
        if (dy < 0.01 && dy > -0.01) {
            System.out.println("The x value is " + x + ". The value of the "
                    + "derivative is "+ dy);
            }
        next_x = x;
        }
    }   
}

The method for finding roots is used for finding zeroes (this definitely works). I only included it inside my test program because I thought that I could somehow use similar logic inside the method which finds derivatives.

求根的方法用于求0(这绝对有效)。我只将它包含在我的测试程序中,因为我认为我可以在方法中使用类似的逻辑来查找导数。

The method for

的方法

public static void findDerivative(Function f, double lowerBound, double 
            upperBound, double step) {
    double x = lowerBound, next_x = x;
    double dy = (f.f(x+step) - f.f(x)) / step;

    for (x = lowerBound; x <= upperBound; x += step) {
        double dx = x - next_x;
        dy = (f.f(x+step) - f.f(x)) / step;
        if (dy < 0.01 && dy > -0.01) {
            System.out.println("The x value is " + x + ". The value of the "
                    + "derivative is "+ dy);
            }
        next_x = x;
        }
    }   

could definitely be improved. How could I write this differently? Here is sample output.

肯定会提高。我怎么能写得不一样呢?下面是示例输出。

The x value is 3.129999999999977. The value of the derivative is -0.006592578364594814
The x value is 3.1399999999999766. The value of the derivative is 0.0034073256197308943
The x value is 6.26999999999991. The value of the derivative is 0.008185181673381337
The x value is 6.27999999999991. The value of the derivative is -0.0018146842631128202
The x value is 9.409999999999844. The value of the derivative is -0.009777764220086915
The x value is 9.419999999999844. The value of the derivative is 2.2203830347677922E-4
The x value is 12.559999999999777. The value of the derivative is 0.0013706082193754021
The x value is 12.569999999999776. The value of the derivative is -0.00862924258597797
The x value is 15.69999999999971. The value of the derivative is -0.002963251265619693
The x value is 15.70999999999971. The value of the derivative is 0.007036644660118885
The x value is 18.840000000000146. The value of the derivative is 0.004555886794943564
The x value is 18.850000000000147. The value of the derivative is -0.005444028885981389
The x value is 21.980000000000636. The value of the derivative is -0.006148510767989279
The x value is 21.990000000000638. The value of the derivative is 0.0038513993028788107
The x value is 25.120000000001127. The value of the derivative is 0.0077411191450771355
The x value is 25.13000000000113. The value of the derivative is -0.0022587599505241585

3 个解决方案

#1


2  

The main thing that I can see to improve performance in the case that f is expensive to compute, you could save the previous value of f(x) instead of computing it twice for each iteration. Also dx is never used and would always be equal to step anyway. next_x also never used. Some variable can be declare inside the loop. Moving the variable declarations inside improves readability but not performance.

在计算f代价高昂的情况下,我能看到的改善性能的主要事情是,可以保存f(x)的前一个值,而不是每次迭代计算两次。而且dx从来没有被使用过而且总是等于步长。next_x也从未使用过。可以在循环中声明某个变量。在内部移动变量声明可以提高可读性,但不能提高性能。

public static void findDerivative(Function f, double lowerBound, double upperBound, double step) {
    double fxstep = f.f(x);

    for (double x = lowerBound; x <= upperBound; x += step) {
        double fx = fxstep;
        fxstep = f.f(x+step);
        double dy = (fxstep - fx) / step;
        if (dy < 0.01 && dy > -0.01) {
            System.out.println("The x value is " + x + ". The value of the "
                    + "derivative is " + dy);
        }
    }
}

#2


0  

The java code you based on (from rosettacode) is not OK, do not depend on it.

基于(来自rosettacode)的java代码不正确,不要依赖它。

  • it's expecting y (a double value) will become exactly zero.
    You need a tolerance value for such kind of tests.
  • 它期望y(一个双数)会变成0。对于此类测试,您需要一个公差值。
  • it's calculating derivative, and using Newton's Method to calculate next x value,
    but not using it to update x, there is not any optimization there.
  • 它是计算导数,用牛顿的方法计算下一个x值,但不是用它来更新x,这里没有任何优化。

Here there is an example of Newton's Method in Java

这里有一个牛顿法在Java中的例子

Yes you can optimize your code using Newton's method,
Since it can solve f(x) = 0 when f'(x) given,
also can solve f'(x) = 0 when f''(x) given, same thing.

是的,你可以用牛顿的方法优化你的代码,因为它可以在给定f'(x)时解f(x) = 0,也可以在给定f'(x) = 0时解f'(x)。

#3


0  

To clarify my comment, I modified the code in the link.
I used step = 2, and got correct results. Check how fast it's, compared to other.
That's why optimization is used,
otherwise reducing the step size and using brute force would do the job.

为了澄清我的评论,我修改了链接中的代码。我使用步骤= 2,得到了正确的结果。看看它比其他的快多少。这就是为什么要使用优化,否则会减少步骤大小并使用蛮力。

class Test {

    static double f(double x) {
        return Math.sin(x);
    }

    static double fprime(double x) {
        return Math.cos(x);
    }

    public static void main(String argv[]) {

        double tolerance = .000000001; // Our approximation of zero
        int max_count = 200; // Maximum number of Newton's method iterations

        /*
         * x is our current guess. If no command line guess is given, we take 0
         * as our starting point.
         */

        double x = 0.6;
        double low = -4;
        double high = 4;
        double step = 2;
        int inner_count = 0;

        for (double initial = low; initial <= high; initial += step) {
            x = initial;
            for (int count = 1; (Math.abs(f(x)) > tolerance)
                    && (count < max_count); count++) {
                inner_count++;
                x = x - f(x) / fprime(x);
            }

            if (Math.abs(f(x)) <= tolerance) {
                System.out.println("Step: " + inner_count + ", x = " + x);
            } else {
                System.out.println("Failed to find a zero");
            }
        }
    }

}

#1


2  

The main thing that I can see to improve performance in the case that f is expensive to compute, you could save the previous value of f(x) instead of computing it twice for each iteration. Also dx is never used and would always be equal to step anyway. next_x also never used. Some variable can be declare inside the loop. Moving the variable declarations inside improves readability but not performance.

在计算f代价高昂的情况下,我能看到的改善性能的主要事情是,可以保存f(x)的前一个值,而不是每次迭代计算两次。而且dx从来没有被使用过而且总是等于步长。next_x也从未使用过。可以在循环中声明某个变量。在内部移动变量声明可以提高可读性,但不能提高性能。

public static void findDerivative(Function f, double lowerBound, double upperBound, double step) {
    double fxstep = f.f(x);

    for (double x = lowerBound; x <= upperBound; x += step) {
        double fx = fxstep;
        fxstep = f.f(x+step);
        double dy = (fxstep - fx) / step;
        if (dy < 0.01 && dy > -0.01) {
            System.out.println("The x value is " + x + ". The value of the "
                    + "derivative is " + dy);
        }
    }
}

#2


0  

The java code you based on (from rosettacode) is not OK, do not depend on it.

基于(来自rosettacode)的java代码不正确,不要依赖它。

  • it's expecting y (a double value) will become exactly zero.
    You need a tolerance value for such kind of tests.
  • 它期望y(一个双数)会变成0。对于此类测试,您需要一个公差值。
  • it's calculating derivative, and using Newton's Method to calculate next x value,
    but not using it to update x, there is not any optimization there.
  • 它是计算导数,用牛顿的方法计算下一个x值,但不是用它来更新x,这里没有任何优化。

Here there is an example of Newton's Method in Java

这里有一个牛顿法在Java中的例子

Yes you can optimize your code using Newton's method,
Since it can solve f(x) = 0 when f'(x) given,
also can solve f'(x) = 0 when f''(x) given, same thing.

是的,你可以用牛顿的方法优化你的代码,因为它可以在给定f'(x)时解f(x) = 0,也可以在给定f'(x) = 0时解f'(x)。

#3


0  

To clarify my comment, I modified the code in the link.
I used step = 2, and got correct results. Check how fast it's, compared to other.
That's why optimization is used,
otherwise reducing the step size and using brute force would do the job.

为了澄清我的评论,我修改了链接中的代码。我使用步骤= 2,得到了正确的结果。看看它比其他的快多少。这就是为什么要使用优化,否则会减少步骤大小并使用蛮力。

class Test {

    static double f(double x) {
        return Math.sin(x);
    }

    static double fprime(double x) {
        return Math.cos(x);
    }

    public static void main(String argv[]) {

        double tolerance = .000000001; // Our approximation of zero
        int max_count = 200; // Maximum number of Newton's method iterations

        /*
         * x is our current guess. If no command line guess is given, we take 0
         * as our starting point.
         */

        double x = 0.6;
        double low = -4;
        double high = 4;
        double step = 2;
        int inner_count = 0;

        for (double initial = low; initial <= high; initial += step) {
            x = initial;
            for (int count = 1; (Math.abs(f(x)) > tolerance)
                    && (count < max_count); count++) {
                inner_count++;
                x = x - f(x) / fprime(x);
            }

            if (Math.abs(f(x)) <= tolerance) {
                System.out.println("Step: " + inner_count + ", x = " + x);
            } else {
                System.out.println("Failed to find a zero");
            }
        }
    }

}