Как повторно реализовать метод sin () в Java? (чтобы результаты были близки к Math.sin ()) - PullRequest
4 голосов
/ 05 июня 2011

Я знаю, Math.sin() может работать, но мне нужно реализовать его самому, используя factorial(int) У меня есть метод факториала, уже приведенный ниже, мой sin метод, но я не могу получить тот же результат, что и Math.sin():

public static double factorial(double n) {
    if (n <= 1) // base case
        return 1;
    else
        return n * factorial(n - 1);
}

public static double sin(int n) {
    double sum = 0.0;
    for (int i = 1; i <= n; i++) {
        if (i % 2 == 0) {
            sum += Math.pow(1, i) / factorial(2 * i + 1);
        } else {
            sum += Math.pow(-1, i) / factorial(2 * i + 1);
        }
    }
    return sum;
}

Ответы [ 4 ]

5 голосов
/ 05 июня 2011

Вы должны использовать серию Тейлор.Отличное руководство здесь

Я вижу, что вы пытались, но ваш метод греха неверен


public static sin(int n) {
    // angle to radians
    double rad = n*1./180.*Math.PI;
    // the first element of the taylor series
    double sum = rad;
    // add them up until a certain precision (eg. 10)
    for (int i = 1; i <= PRECISION; i++) {
        if (i % 2 == 0) 
            sum += Math.pow(rad, 2*i+1) / factorial(2 * i + 1);
        else 
            sum -= Math.pow(rad, 2*i+1) / factorial(2 * i + 1);
    }
    return sum;
}

Рабочий пример вычисления функции sin.Извините, я записал это в C ++, но надеюсь, вы поняли картину.Это не так уж отличается:)

3 голосов
/ 05 июня 2011

Похоже, вы пытаетесь использовать расширение серии Тейлора для греха , но не включили термин для x. Поэтому ваш метод всегда будет пытаться приблизить sin (1) независимо от аргумента.

Параметр метода контролирует только точность. В хорошей реализации приемлемое значение для этого параметра определяется автоматически, не позволяя вызывающей стороне переходить к низкому значению, что может привести к очень неточным результатам при большом x. Кроме того, чтобы помочь быстрой конвергенции (и предотвратить ненужную потерю значимости ) ряда, реализации обычно используют этот sin (x + k * 2 * PI) = sin (x) для первого перемещения x в диапазон [-PI, PI].

Кроме того, ваш метод не очень эффективен из-за повторных оценок факториалов. (Для оценки факториала (5) вы вычисляете факториал (3), который вы уже вычислили в предыдущей итерации цикла for).

Наконец, обратите внимание, что ваша факториальная реализация принимает аргумент типа double, но он верен только для целых чисел, и ваш метод sin, вероятно, должен получить угол как double.

3 голосов
/ 05 июня 2011

Ваша формула неверна, и вы получаете приблизительный результат sin (1), и все, что вы делаете, изменяя n, изменяет точность этого вычисления. Вы должны посмотреть формулу в Википедии, и там вы увидите, что ваш n находится не в том месте и не должен использоваться как предел цикла for, а скорее в числителе дроби, в Math.pow ...) метод. Выезд Серия Тейлор

2 голосов
/ 17 июля 2016

Грех (х) может быть представлен как ряд Тейлора :

Грех (х) = (х / 1!) - (х 3 / 3!) + (Х 5 / 5!) - (х 7 / 7!) +…

Так что вы можете написать свой код так:

public static double getSine(double x) {
    double result = 0;

    for (int i = 0, j = 1, k = 1; i < 100; i++, j = j + 2, k = k * -1) {
        result = result + ((Math.pow(x, j) / factorial (j)) * k);
    }
    return result;
}

Здесь мы запустили наш цикл только 100 раз. Если вы хотите выполнить больше, вам нужно изменить базовое уравнение (в противном случае произойдет значение бесконечности).

Я узнал очень хороший трюк из книги Р.Г.Дромея «Как решить ее с помощью компьютера». Он объясняет это так:

(x 3 / 3!) = (X X x X x) / (3 X 2 X 1) = (x 2 / (3 X 2)) X (x 1 / 1!) i = 3
(x 5 / 5!) = (x X x X x X x X x) / (5 X 4 X 3 X 2 X 1) = (x 2 / (5 X 4)) X (x 3 / 3!) I = 5
7 / 7!) = (х х х х х х х х х х х х х) / (7 Х 6 Х 5 Х 4 Х 3 Х 2 Х 1) = (х 2 / (7 X 6)) X (x 5 / 5!) I = 7

Таким образом, термины (х 2 / (3 х 2)), (х 2 / (5 х 4)), (х 2 / (7 X 6)) можно выразить как
x 2 / (i X (i - 1)) для i = 3,5,7,…

Поэтому для генерации последовательных членов ряда синусов мы можем написать:
ток i th term = (x 2 / (i X (i - 1))) X (предыдущий термин)

Код следующий:

public static double getSine(double x) {
    double result = 0;
    double term = x;

    result = x;
    for (int i = 3, j = -1; i < 100000000; i = i + 2, j = j * -1) {
        term = x * x * term / (i * (i - 1));
        result = result + term * j;
    }

    return result;
}

Обратите внимание, что переменная j используется для чередования знака термина.

...