Calculando funciones trigonométricas sin librerías (series de taylor) (Aporte)

Iniciado por engel lex, 26 Octubre 2015, 16:56 PM

0 Miembros y 1 Visitante están viendo este tema.

engel lex

Debido a una duda planteada por un usuario me decidí hacer este tema

Como calcular un seno o un coseno sin necesidad de una librería externa, sino por nuestros propios medios...

para esto haremos uso de las Series de taylor no caeré en detalles sobre esta teoría sino iré directamente a su aplicación... en este caso usaré solo la biblioteca iostream para impresión de datos

/*************************** cabecera ***************************/
Código (cpp) [Seleccionar]
#include <iostream>
using namespace std;

const double PI = 3.14159;
const double EPSILON = 0.00001;

/****************************************************************/

luego las series de Taylor para esto me solicitan 2 funciones puntuales, potencias y factoriales, para el caso de las potencias, solo necesito exponentes enteros, en los factoriales solo valores enteros (los que nos quita mucho trabajo de encima), aquí estan mi forma de aplicarlas (tambien un par de funciones extra, para conversión de grados a radianes y de modulo)

/*************************** funciones base ***************************/

Código (cpp) [Seleccionar]
double angulos_a_radianes(double angulo) {
return (angulo * PI) / 180;
}

double potencia(double base, int exp) {
//si el exponente es 0 o la base es 1, el resultado es directamente 1
if(exp == 0 || base == 1){return 1;}

//si la base es -1, el resultado es 1 o -1, para reflejar esto lo hago:
//(si el exponente es par exp%2 es 0) 1 - (0)*2 = 1-0 = 1
//(si el exponente es impar exp%2 es 1) 1 - (1)*2 = 1-2 = -1
if(base == -1){return 1-(exp%2)*2;}

//cargamos el resultado a la base
double resultado = base;

//siempre que el exponente sea mayor que 1 seguimos
while(exp-- > 1){
resultado*=base;
}
return resultado;
}

long int factorial(int factor){
//el factorial de 0 es 1
if(factor == 0) return 1;

//cargamos 1 para multiplicar
long int resultado = 1;

//hacemos... mientras factor sea mayor que 1
do{
resultado*=factor;
}while(factor-- > 1);

return resultado;
}

//retornamos como valor positivo siempre (función módulo)
double positivo(double numero){
if(numero<0) return -1*numero;
return numero;
}


/*************************** funciones base ***************************/

ahora vamos a calcular directamente los valores que nos importa, seno, coseno y tangente

/*************************** funciones con taylor ***************************/

Código (cpp) [Seleccionar]
//taylor para seno, con x en radianes
double seno(double x){
//aqui llevaremos la sumatoria
double resultado=0;

//usaremos este valor de control para nuestra precisión
double resultado_anterior=0;

//este es el variable de la sumatoria
int sumador = 0;

//hacemos mientras la diferencia sea menor a la precisión
do{
//almacenamos el resultado anterior
resultado_anterior = resultado;

//la serie de taylor
resultado+= potencia(-1,sumador)*potencia(x,2*sumador+1)/factorial(2*sumador+1);

//siempre avanzamos 1
sumador++;

}while(positivo(resultado-resultado_anterior) >= EPSILON);

return resultado;
}

//taylor para coseno, con x en radianes
double coseno(double x){
//aqui llevaremos la sumatoria
double resultado=0;

//usaremos este valor de control para nuestra precisión
double resultado_anterior=0;

//este es el variable de la sumatoria
int sumador = 0;

//hacemos mientras la diferencia sea menor a la precisión
do{
//almacenamos el resultado anterior
resultado_anterior = resultado;

//la serie de taylor
resultado+= potencia(-1,sumador)*potencia(x,2*sumador)/factorial(2*sumador);

//siempre avanzamos 1
sumador++;

}while(positivo(resultado-resultado_anterior) >= EPSILON);

return resultado;
}

double tangente(double x){

if(x/(PI/2)== (int)(x/(PI/2))){
cout << "Error: el angulo no debe ser multiplo de 90" << endl;
return 0;
}

//trampa... pero la serie de taylor para tangente es computacionalmente muy pesada
//este metodo aunque ligeramente menos preciso, resuelve el problema mucho más facil
return seno(x)/coseno(x);
}


/*************************** funciones con taylor ***************************/

y ya lo unico que queda por agregar es el main con la llamada a estas funciones, recomiendo mantener la precisión de cout en la misma cantidad de decimales de PI y EPSILON, ya que esto es lo que nos mantendrá en un "bonito" margen de precisión... tambien recuerden que si piden mucha precisión empezará a fallar por la forma en que funciona float a nivel binario en ese caso tendrán que usar aritmetica de precisión arbitraria (aquí un tema que hice sobre eso hace tiempo)

Código (cpp) [Seleccionar]
int main(){
cout.precision(5);
double calculo = angulos_a_radianes(30);
calculo = seno(calculo);
cout << calculo << endl;
return 0;
}


espero que les sirva!
El problema con la sociedad actualmente radica en que todos creen que tienen el derecho de tener una opinión, y que esa opinión sea validada por todos, cuando lo correcto es que todos tengan derecho a una opinión, siempre y cuando esa opinión pueda ser ignorada, cuestionada, e incluso ser sujeta a burla, particularmente cuando no tiene sentido alguno.

furciorifa

Vaya excelente aporte, yo aportaré mis códigos, tengo un método numérico para calcular derivadas , integrales , y cosas poderosas sin librerías, espero que podamos sacar esta comunidad adelante, no hay una especie de chat es lo malo.