Foro de elhacker.net

Programación => Programación C/C++ => Mensaje iniciado por: engel lex en 26 Octubre 2015, 16:56 pm



Título: Calculando funciones trigonométricas sin librerías (series de taylor) (Aporte)
Publicado por: engel lex en 26 Octubre 2015, 16:56 pm
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 (https://es.wikipedia.org/wiki/Serie_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
  1. #include <iostream>
  2. using namespace std;
  3.  
  4. const double PI = 3.14159;
  5. 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
  1. double angulos_a_radianes(double angulo) {
  2. return (angulo * PI) / 180;
  3. }
  4.  
  5. double potencia(double base, int exp) {
  6. //si el exponente es 0 o la base es 1, el resultado es directamente 1
  7. if(exp == 0 || base == 1){return 1;}
  8.  
  9. //si la base es -1, el resultado es 1 o -1, para reflejar esto lo hago:
  10. //(si el exponente es par exp%2 es 0) 1 - (0)*2 = 1-0 = 1
  11. //(si el exponente es impar exp%2 es 1) 1 - (1)*2 = 1-2 = -1
  12. if(base == -1){return 1-(exp%2)*2;}
  13.  
  14. //cargamos el resultado a la base
  15. double resultado = base;
  16.  
  17. //siempre que el exponente sea mayor que 1 seguimos
  18. while(exp-- > 1){
  19. resultado*=base;
  20. }
  21. return resultado;
  22. }
  23.  
  24. long int factorial(int factor){
  25. //el factorial de 0 es 1
  26. if(factor == 0) return 1;
  27.  
  28. //cargamos 1 para multiplicar
  29. long int resultado = 1;
  30.  
  31. //hacemos... mientras factor sea mayor que 1
  32. do{
  33. resultado*=factor;
  34. }while(factor-- > 1);
  35.  
  36. return resultado;
  37. }
  38.  
  39. //retornamos como valor positivo siempre (función módulo)
  40. double positivo(double numero){
  41. if(numero<0) return -1*numero;
  42. return numero;
  43. }

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

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

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

Código
  1. //taylor para seno, con x en radianes
  2. double seno(double x){
  3. //aqui llevaremos la sumatoria
  4. double resultado=0;
  5.  
  6. //usaremos este valor de control para nuestra precisión
  7. double resultado_anterior=0;
  8.  
  9. //este es el variable de la sumatoria
  10. int sumador = 0;
  11.  
  12. //hacemos mientras la diferencia sea menor a la precisión
  13. do{
  14. //almacenamos el resultado anterior
  15. resultado_anterior = resultado;
  16.  
  17. //la serie de taylor
  18. resultado+= potencia(-1,sumador)*potencia(x,2*sumador+1)/factorial(2*sumador+1);
  19.  
  20. //siempre avanzamos 1
  21. sumador++;
  22.  
  23. }while(positivo(resultado-resultado_anterior) >= EPSILON);
  24.  
  25. return resultado;
  26. }
  27.  
  28. //taylor para coseno, con x en radianes
  29. double coseno(double x){
  30. //aqui llevaremos la sumatoria
  31. double resultado=0;
  32.  
  33. //usaremos este valor de control para nuestra precisión
  34. double resultado_anterior=0;
  35.  
  36. //este es el variable de la sumatoria
  37. int sumador = 0;
  38.  
  39. //hacemos mientras la diferencia sea menor a la precisión
  40. do{
  41. //almacenamos el resultado anterior
  42. resultado_anterior = resultado;
  43.  
  44. //la serie de taylor
  45. resultado+= potencia(-1,sumador)*potencia(x,2*sumador)/factorial(2*sumador);
  46.  
  47. //siempre avanzamos 1
  48. sumador++;
  49.  
  50. }while(positivo(resultado-resultado_anterior) >= EPSILON);
  51.  
  52. return resultado;
  53. }
  54.  
  55. double tangente(double x){
  56.  
  57. if(x/(PI/2)== (int)(x/(PI/2))){
  58. cout << "Error: el angulo no debe ser multiplo de 90" << endl;
  59. return 0;
  60. }
  61.  
  62. //trampa... pero la serie de taylor para tangente es computacionalmente muy pesada
  63. //este metodo aunque ligeramente menos preciso, resuelve el problema mucho más facil
  64. return seno(x)/coseno(x);
  65. }

/*************************** 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 (https://foro.elhacker.net/programacion_cc/calculo_de_pi_en_alta_precision_aporte-t412338.0.html;msg1934259#msg1934259) que hice sobre eso hace tiempo)

Código
  1. int main(){
  2. cout.precision(5);
  3. double calculo = angulos_a_radianes(30);
  4. calculo = seno(calculo);
  5. cout << calculo << endl;
  6. return 0;
  7. }

espero que les sirva!


Título: Re: Calculando funciones trigonométricas sin librerías (series de taylor) (Aporte)
Publicado por: furciorifa en 1 Noviembre 2015, 05:41 am
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.