Вычисление sin (x) w / oMath и использование циклов только в java

Я должен вычислить математику.sin (x) с использованием серии Тейлора:

n

∑ (-1)^i* (x^(2i+1) / (2i+1)!) для n → ∞

i=0

поэтому мне разрешено использовать только циклы (без рекурсии), и я не могу использовать класс Math. Вот как далеко я зашел:--2-->

public double sinLoops(double x) {
        int potenz1;
        double potenz2 = x;
        double fac = 1;
        double result = 0;

        do {
            if ((i % 2) == 0) {
                potenz1 = 1;
            } else {
                potenz1 = (-1);
            }

            for (int counter = 1; counter < (2 * i + 1); counter++) {
                potenz2 *= x;
            }
            for (int counter2 = (2 * i + 1); counter2 >= 1; counter2--) {
                fac *= counter2;
            }   
            result += potenz1 * potenz2 / fac;
            i++;
        } while (result > 0.0000001 || result < -0.0000001);
        return result;
    }

однако я думаю, что мое условие разрыва не совсем правильно (-1*10^-7 или 1*10^-7),возвращаемый результат-NaN. Я уже посмотрел, но сейчас я немного перегружен, поэтому я надеюсь, что кто-то может мне помочь. :)

спасибо заранее!

3 ответов


  1. вы не инициализировали i.
  2. вы проверили окончательное условие против результата, а не этого элемента Тейлора суммы.
  3. вы оставили элементы potenz2 и fac, чтобы держать спираль из-под контроля, а не сбрасывать их для каждого нового элемента в серии.
  4. В конце концов они достигнут бесконечности и бесконечности, разделят их и получат NaN. NaN, добавленный к текущему результату, - NaN, и это фактически возвращает true для условного и loop (NaN имеет нечетные эффекты с условными обозначениями).

вот рабочий код с комментариями по проблемам.

    public double sinLoops(double x) {
        int i = 0; //this didn't exist.
        double result = 0;
        double seriesElement; //You need the individual taylor series element.
        do {
            double potenz2 = x; //these need to be reset each time.
            double fac = 1; //if not they overflow and infinity/infinity is NaN and it exits.
            int potenz1 = ((i & 1) == 1) ? -1 : 1; //this is just short hand.
            for (int counter = 1; counter < (2 * i + 1); counter++) {
                potenz2 *= x;
            }
            for (int counter2 = (2 * i + 1); counter2 >= 1; counter2--) {
                fac *= counter2; //we could actually keep the last iteration and do 2*(i-1)+1 to 2*i+1 each new i.
            }
            seriesElement = potenz1 * potenz2 / fac; //we need to save the value here.

            result += seriesElement; //we are summing them in the results.
            i++;

        } while (seriesElement > 0.0000001 || seriesElement < -0.0000001); //We check this conditional against the series element, *NOT THE RESULT*
        return result;
    }

в случае, если кому - то это нужно как-то для какой-то производственной работы с критической скоростью (и менее неправильный ответ, хотя на самом деле в этом случае используйте математику), а не "может ли кто-нибудь сделать мою домашнюю работу для меня", вот оптимизированный код:

public double sinLoops(double x) {
        double result = 0, powerx = -1, fac = 1;
        int i = 0, n, m = 0;
        while (true) {
            n = m;
            m = (i++*2) + 1;
            powerx *= -1;
            while (n < m) {
                powerx *= x;
                fac *= ++n;
            }
            if ((Double.isInfinite(fac)) || (Double.isInfinite(powerx))) break;
            result += powerx / fac;
        }
        return result;
    }

вы не изменяете значение переменной результата:)

также переменная i не объявлена. Действительно, было бы намного проще, если бы вы разместили образец рабочего кода.

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

У вас тоже есть пара ошибок, таких как ошибка off-by-one в счетчиках циклов и не повторная инициализация накапливающихся переменных между итерациями цикла. Он легко анализируется, проходя через случай аргументов 0 и Pi.


интересный вопрос. Как Tatarize, я надеюсь, что этого не избежать домашней работы.

этот код выглядит как предвестник количества терминов, необходимых для обеспечения абсолютной точности +/- 0.000 000 1 в результате для всех углов 0-90 градусов.

термин наивысшей мощности имеет значение x^k / k! к результату. Так что

            x^k / k!  < 1 / 10^7

здесь х-в радианах, поэтому наибольшее значение x ~ 1.57 рад. Это означает, что только серия до мощности 13 даст вам окончательный срок менее 0.000 000 1 .

к сожалению, мой компьютер пожилого возраста (32-бит) и любая попытка вычислить 13! вызывает переполнение. Поэтому я адаптирую способ Хорнер немного, возможно теряющ некоторую эффективность но избегающ факториального переполнения и позволяющ остановке если угол небольшой или если адекватная точность приобретена перед силой 13.

  Sin x = x - x^2(x/3! - x^2(x/5! - x^2(x/7! - . . . - x^2(x/(m-1)!- x^2(x/m!) 

где m самая высокая сила необходима для пожеланной абсолютной точности.

  Sin x = x + Sum { iTerm(i) * x^2 / (i * (i-1)) }

где

  iTerm(0) = x and iTerm(n) = - x^2 * iTerm(n-1)/(i*(i-1)

PS-почему мы не можем использовать математическое форматирование вне обмена стеком математики ? Это сделало бы написание уравнений намного яснее.

public class TrigByPoly
{
    // No constructor used.

    public static void main(String[] args)
    {
        double x0 = 0, 
               x1 = Math.PI/12, 
               x2 = Math.PI/6,  
               x3 = Math.PI/4,
               x4 = Math.PI/3,
               x5 = Math.PI/2;

        double sinx0 = SinByPoly(x0),
               sinx1 = SinByPoly(x1),
               sinx2 = SinByPoly(x2),
               sinx3 = SinByPoly(x3),
               sinx4 = SinByPoly(x4),
               sinx5 = SinByPoly(x5); 

        System.out.println("Sin(0) to 7 decimal places is : " + sinx0);
        System.out.println("Sin(15) to 7 decimal places is : " + sinx1);
        System.out.println("Sin(30) to 7 decimal places is : " + sinx2);
        System.out.println("Sin(45) to 7 decimal places is : " + sinx3);
        System.out.println("Sin(60) to 7 decimal places is : " + sinx4);
        System.out.println("Sin(90) to 7 decimal places is : " + sinx5);
    }

    public static double SinByPoly(double x)
    {
        int i = 0;  // Polynomial order indicator.

        double x2 = x * x,
               iTerm,   
               sinx = 0;

        if (x < 0.0084) // Limiting angle for Sinx = x to 10^-7 precision.
            sinx = x;
        else
        {
            sinx = x;
            iTerm = sinx;
            i = 3;
            do
            {
                iTerm = - x2 * iTerm / ( i * (i - 1));
                sinx += iTerm;
                i = i + 2;
            } while (i < 14 && (iTerm > 0.0000001 || -iTerm > 0.0000001));
        }
        return sinx;
    }
}

OUTPUT
======

Sin(0) to an absolute precision of 1.0E-7 is : 0.0
Sin(15) to an absolute precision of 1.0E-7 is : 0.2588190618109834
Sin(30) to an absolute precision of 1.0E-7 is : 0.4999999918690232
Sin(45) to an absolute precision of 1.0E-7 is : 0.7071067829368671
Sin(60) to an absolute precision of 1.0E-7 is : 0.8660254450997811
Sin(75) to an absolute precision of 1.0E-7 is : 0.9659258210120795
Sin(90) to an absolute precision of 1.0E-7 is : 0.999999943741051