Я следовал руководству, которое дал нам мой профессор, но просто не могу найти, где я ошибся. Я также ответил на некоторые другие вопросы о реализации серии Тейлора на C.

enter image description here

Просто предположите, что RaiseTo (возвести число в степень x) есть.

double factorial (int n)
{
    int fact = 1,
    flag;

   for (flag = 1; flag <= n; flag++)
   {
        fact *= flag;
   }

   return flag;
}

double sine (double rad)
{

int flag_2,
    plusOrMinus2 = 0; //1 for plus, 0 for minus 
double sin, 
    val2 = rad,
    radRaisedToX2,
    terms;

terms = NUMBER_OF_TERMS; //10 terms

    for (flag_2 = 1; flag_2 <= 2 * terms; flag_2 += 2)
    {
        radRaisedToX2 = RaiseTo(rad, flag_2);   

        if (plusOrMinus2 == 0)
        {
            val2 -=  radRaisedToX2/factorial(flag_2);
            plusOrMinus2++; //Add the next number
        }

        else
        {
            val2 += radRaisedToX2/factorial(flag_2);
            plusOrMinus2--; //Subtract the next number
        }
    }

    sin = val2;
    return sin;
 }

int main()
{
    int degree;
    scanf("%d", &degree);
    double rad, cosx, sinx;
    rad = degree * PI / 180.00;
    //cosx = cosine (rad);
    sinx = sine (rad);
    printf("%lf \n%lf", rad, sinx);
}

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

1
Jonn Ralge Yuvallos 25 Ноя 2016 в 19:04

3 ответа

Лучший ответ

Все, что превышает 12!, больше, чем может поместиться в 32-битный int, поэтому такие значения будут переполняться и, следовательно, не вернут то, что вы ожидаете.

Вместо того, чтобы каждый раз вычислять полный факториал, взгляните на каждый член в последовательности относительно предыдущего. Для любого данного термина следующий член в -((x*x)/(flag_2*(flag_2-1)) раз больше предыдущего. Итак, начните с члена x, затем умножьте на этот множитель для каждого последующего члена.

Также есть уловка, позволяющая вычислить результат с точностью до double, не зная, сколько терминов вам нужно. Я оставлю это читателю в качестве упражнения.

3
dbush 25 Ноя 2016 в 16:23

В функции factorial вы выполняете умножение int перед присвоением возвращаемому значению функции double. Факториалы могут легко нарушить диапазон int, например 20! = 2432902008176640000.

Вы также вернули неправильную переменную - счетчик цикла!

Измените локальную переменную на double, поскольку

double factorial (int n)
{
    double fact = 1;
    int flag;

    for (flag = 1; flag <= n; flag++)
    {
        fact *= flag;
    }
    return fact;    // it was the wrong variable, and wrong type
}

Кроме того, нет необходимости даже в факторном вычислении. Обратите внимание, что каждый член ряда умножает предыдущий член на rad и делит на номер члена - со сменой знака.

1
Weather Vane 25 Ноя 2016 в 16:27

Другой довольно наивный 5-минутный подход включает в себя вычисление таблицы поиска, которая содержит первые 20 или около того факториалов, то есть 1! .. 20! Это требует очень мало памяти и может увеличить скорость по сравнению с методом вычислений «каждый раз». Дальнейшая оптимизация может быть легко реализована в функции, которая предварительно вычисляет факториалы, используя соотношение между каждым из них и предыдущим.

Подход, который эффективно устраняет ветвление (если X делает Y, иначе делает Z) в циклах двух триггерных функций, снова обеспечивает еще большую скорость.

Код C

#include <stdlib.h>
#include <stdio.h>
#include <math.h>

const int nMaxTerms=20;
double factorials[nMaxTerms];

double factorial(int n)
{
    if (n==1)
        return 1;
    else
        return (double)n * factorial(n - 1.0);
}

void precalcFactorials()
{
    for (int i=1; i<nMaxTerms+1; i++)
    {
        factorials[i-1] = factorial(i);
    }
}

/*
    sin(x) = x - (x^3)/3! + (x^5)/5! - (x^7)/7! .......
*/
double taylorSine(double rads)
{
    double result = rads;

    for (int curTerm=1; curTerm<=(nMaxTerms/2)-1; curTerm++)
    {
        double curTermValue = pow(rads, (curTerm*2)+1);
        curTermValue /= factorials[ curTerm*2 ];
        if (curTerm & 0x01)
            result -= curTermValue;
        else
            result += curTermValue;
    }
    return result;
}

/*
    cos(x) = 1 - (x^2)/2! + (x^4)/4! - (x^6)/6! .......
*/
double taylorCos(double rads)
{
    double result = 1.0;
    for (int curTerm=1; curTerm<=(nMaxTerms/2)-1; curTerm++)
    {
        double curTermValue = pow(rads, (curTerm*2) );
        curTermValue /= factorials[ (curTerm*2) - 1 ];
        if (curTerm & 0x01)
            result -= curTermValue;
        else
            result += curTermValue;
    }
    return result;
}

int main()
{
    precalcFactorials();
    printf("Math sin(0.5) = %f\n", sin(0.5));
    printf("taylorSin(0.5) = %f\n", taylorSine(0.5));

    printf("Math cos(0.5) = %f\n", cos(0.5));
    printf("taylorCos(0.5) = %f\n", taylorCos(0.5));

    return 0;
}

Выход

Math sin(0.5) = 0.479426
taylorSin(0.5) = 0.479426
Math cos(0.5) = 0.877583 
taylorCos(0.5) = 0.877583

Javascript

Реализованный на javascript, код дает, казалось бы, идентичные результаты (я не очень много тестировал) встроенной библиотеке Math при суммировании всего 7 членов в функциях sin / cos.

window.addEventListener('load', onDocLoaded, false);

function onDocLoaded(evt)
{
	console.log('starting');
	for (var i=1; i<21; i++)
		factorials[i-1] = factorial(i);
	console.log('calculated');
	
	console.log(" Math.cos(0.5) = " + Math.cos(0.5));
	console.log("taylorCos(0.5) = " + taylorCos(0.5));
	console.log('-');
	console.log("  Math.sin(0.5) = " + Math.sin(0.5));
	console.log("taylorSine(0.5) = " + taylorSine(0.5));
}

var factorials = [];

function factorial(n)
{
	if (n==1)
		return 1;
	else
		return n * factorial(n-1);
}

/*
	sin(x) = x - (x^3)/3! + (x^5)/5! - (x^7)/7! .......
*/
function taylorSine(x)
{
	var result = x;
	for (var curTerm=1; curTerm<=7; curTerm++)
	{
		var curTermValue = Math.pow(x, (curTerm*2)+1);
		curTermValue /= factorials[ curTerm*2 ];
		if (curTerm & 0x01)
			result -= curTermValue;
		else
			result += curTermValue;
	}
	return result;
}

/*
	cos(x) = 1 - (x^2)/2! + (x^4)/4! - (x^6)/6! .......
*/
function taylorCos(x)
{
	var result = 1.0;
	for (var curTerm=1; curTerm<=7; curTerm++)
	{
		var curTermValue = Math.pow(x, (curTerm*2));
		curTermValue /= factorials[ (curTerm*2)-1 ];
		if (curTerm & 0x01)
			result -= curTermValue;
		else
			result += curTermValue;
	}
	return result;
}
0
enhzflep 25 Ноя 2016 в 17:19