Приближенная производная для непрерывной функции в течение определенных интервалов шага
Я хочу написать метод в Java, который находит производную для непрерывной функции. Это некоторые предположения, которые были сделаны для метода -
- Функция непрерывна от x = 0 до x = бесконечность.
- Производная существует в каждом интервале.
- Размер шага должен быть определен как параметр.
- Метод найдет max/min для непрерывной функции по заданному интервалу [a: b].
В качестве примера можно показать, что функция cos (x) имеет максимум или минимум при 0, pi, 2pi, 3pi,... npi.
Я хочу написать метод, который найдет все эти максимумы или минимумы, если будут предоставлены функции, lowerBound, upperBound и размер шага.
Чтобы упростить мой тестовый код, я написал программу для 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;
}
}
}
Метод поиска корней используется для нахождения нулей (это определенно работает). Я включил его только в свою тестовую программу, потому что думал, что могу как-то использовать подобную логику внутри метода, который находит производные.
Метод
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 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
Ответы
Ответ 1
Главное, что я могу улучшить для производительности в случае, когда 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
Код java, на котором вы основаны (из rosettacode), не в порядке, не зависит от него.
- Ожидание y (двойное значение) станет равным нулю.
Для таких тестов вам необходимо значение допуска.
- вычислить производную и использовать Newton Method для вычисления следующего значения x,
но не используя его для обновления x, там нет никакой оптимизации.
Здесь приведен пример метода Newton в Java
Да, вы можете оптимизировать свой код с помощью метода Ньютона,
Поскольку он может решить f (x) = 0, если f '(x),
также может решить f '(x) = 0, если f' '(x), то же самое.
Ответ 3
Чтобы прояснить мой комментарий, я изменил код в ссылке.
Я использовал step = 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 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");
}
}
}
}