Foro de elhacker.net

Programación => Programación C/C++ => Mensaje iniciado por: Zeta255 en 23 Marzo 2021, 19:07 pm



Título: [C++] Series de Taylor: sen(x)
Publicado por: Zeta255 en 23 Marzo 2021, 19:07 pm
Hola tengo una duda, me pide hacer una funcion de sen con sumatoria, para eso ingresare un valor x y me dara el valor con sumatoria pero las funciones tiene el mismo criterio de numero impar pero los signos +- son intercarlados como podria hacer para que me salga de igual manera;
Código
  1.  
  2.  
  3. //Solicite al usuario el valor de x y calcule e^x
  4. #include<iostream>
  5. #include<iomanip>
  6. using namespace std;
  7.  
  8. int main()
  9. {
  10. double x, num=1,den=1,f=1,signo;
  11. cout<<"INGRESE el valor de x para sen: ";
  12. cin>>x;
  13. cout<<setprecision(4);
  14. for(int i=1;i<=360;i++)
  15. {
  16. num = num*x;
  17. den = den*i;
  18. f =( f + num/den);
  19.  
  20. }
  21.  
  22. cout<<"EL valor de sen"<<x<<" = "<<f;
  23.  
  24. }


Título: Re: Ayuda con codigo de c++
Publicado por: K-YreX en 23 Marzo 2021, 21:32 pm
La verdad es que la explicación es un poco complicada para el que no sepa de qué le están hablando.
Intuyo que la historia empieza por las series de Taylor y su forma de aproximar funciones como el sen(x) mediante una sumatoria.
Para ver la fórmula de la serie de Taylor para sen(x): https://es.wikipedia.org/wiki/Serie_de_Taylor#Funciones_trigonom%C3%A9tricas
La fórmula dice algo así como:
Código:
sen(x) = SUM(n=0->inf) ((-1)^n / (2n+1)! * x^(2n+1)) =
       = x^1/1! - x^3/3! + x^5/5! - x^7/7! + x^9/9! - ...
Ahora igual ya sí podemos empezar a hacer algo...

Si te das cuenta tú estás calculando:
Código:
sen(x) = 1+x^1/1 + x^2/2 + x^3/4 + x^4/16 + ... 
Por lo que parece que ahí fallan más cosas.

De todas formas, cambiar el signo es muy sencillo. Aunque no nos demos cuenta, cuando cambiamos el signo de algo lo estamos multiplicando por -1. Entonces:
Código
  1. int numero = 5;
  2. for(int i = 0; i < 10; ++i)
  3.  cout << numero << " ";
  4.  numero *= -1; // numero = numero * -1
  5. }
Salida:
Código:
5 -5 5 -5 5 -5 5 -5 5 -5


Título: Re: Ayuda con codigo de c++
Publicado por: Zeta255 en 23 Marzo 2021, 23:14 pm
segun lo que entendi va asi, pero no me compila no se que estare haciendo mal, me tira nan.

Código
  1. #include<iostream>
  2. #include <math.h>
  3. #include<iomanip>
  4.  
  5. using namespace std;
  6. int main(){
  7. int x, numerador=1, denominador=1, sen,f=0;
  8.  
  9. cout<<"ingrese el valor de x para sen: "; cin>>x;
  10. cout<<setprecision(4);
  11. for (int i=1; i<=10;i++)
  12. {
  13. numerador=numerador*(-1^x);
  14. denominador=denominador*(2*i+1);
  15. sen=(numerador/denominador)*(x^(2*x+1));
  16. f=f+sen;
  17. }
  18. cout<<"el valor de sen"<<x<<"= "<<f;
  19. }


Título: Re: [C++] Series de Taylor: sen(x)
Publicado por: K-YreX en 24 Marzo 2021, 07:29 am
Lo primero: avisarte de que he borrado el otro tema que has abierto para el mismo problema.
Lo segundo: es mejor si los títulos de los temas son algo más descriptivos así que he cambiado el título de este tema.

En C++ el operador ^ es un XOR (OR exclusivo) que funciona bit a bit. Para usar potencias hay que utilizar la función pow(). Podrías hacerlo así limitándote a usar la fórmula con potencias y factoriales pero la cantidad de cálculos que va a tener que hacer tu programa va a ser muy grande.

Lo mejor es aprovechar que tu programa es iterativo para ahorrarte operaciones que ya tienes.
Piensa que si en una iteración tienes 1! y lo multiplicas por 2, ya tienes 2!. Si 2! lo multiplicas por 3, ya tienes 3!; y así sucesivamente sin tener que hacer todas las operaciones en cada iteración.
Lo mismo pasa con una potencia: si tienes x y lo multiplicas por x, ya tienes x^2. Si x^2 lo multiplicas por x, ya tienes x^3 y así sucesivamente también.

Ahora tienes que ver cómo ir almacenando estos resultados parciales en cada iteración.
No puedo ser más concreto ahora mismo porque no tengo tiempo.


Título: Re: [C++] Series de Taylor: sen(x)
Publicado por: BloodSharp en 24 Marzo 2021, 15:20 pm
Esta es la implementación de la función seno multiplataforma de ReactOS, espero que te sirva:

Código
  1. /*
  2.   * COPYRIGHT:        See COPYING in the top level directory
  3.   * PROJECT:          ReactOS CRT
  4.   * FILE:             lib/sdk/crt/math/sin.c
  5.   * PURPOSE:          Generic C Implementation of sin
  6.   * PROGRAMMER:       Timo Kreuzer (timo.kreuzer@reactos.org)
  7.   */
  8.  
  9. #ifdef _MSC_VER
  10. #pragma warning(suppress:4164) /* intrinsic not declared */
  11. #pragma function(sin)
  12. #endif /* _MSC_VER */
  13.  
  14. #define PRECISION 9
  15. #define M_PI 3.141592653589793238462643
  16.  
  17. static double sin_off_tbl[] = {0.0, -M_PI/2., 0, -M_PI/2.};
  18. static double sin_sign_tbl[] = {1,-1,-1,1};
  19.  
  20. double
  21. sin(double x)
  22. {
  23.     int quadrant;
  24.     double x2, result;
  25.  
  26.     /* Calculate the quadrant */
  27.     quadrant = (int)(x * (2./M_PI));
  28.  
  29.     /* Get offset inside quadrant */
  30.     x = x - quadrant * (M_PI/2.);
  31.  
  32.     /* Normalize quadrant to [0..3] */
  33.     quadrant = (quadrant - 1) & 0x3;
  34.  
  35.     /* Fixup value for the generic function */
  36.     x += sin_off_tbl[quadrant];
  37.  
  38.     /* Calculate the negative of the square of x */
  39.     x2 = - (x * x);
  40.  
  41.     /* This is an unrolled taylor series using <PRECISION> iterations
  42.       * Example with 4 iterations:
  43.       * result = 1 - x^2/2! + x^4/4! - x^6/6! + x^8/8!
  44.       * To save multiplications and to keep the precision high, it's performed
  45.       * like this:
  46.       * result = 1 - x^2 * (1/2! - x^2 * (1/4! - x^2 * (1/6! - x^2 * (1/8!))))
  47.       */
  48.  
  49.     /* Start with 0, compiler will optimize this away */
  50.     result = 0;
  51.  
  52. #if (PRECISION >= 10)
  53.     result += 1./(1.*2*3*4*5*6*7*8*9*10*11*12*13*14*15*16*17*18*19*20);
  54.     result *= x2;
  55. #endif
  56. #if (PRECISION >= 9)
  57.     result += 1./(1.*2*3*4*5*6*7*8*9*10*11*12*13*14*15*16*17*18);
  58.     result *= x2;
  59. #endif
  60. #if (PRECISION >= 8)
  61.     result += 1./(1.*2*3*4*5*6*7*8*9*10*11*12*13*14*15*16);
  62.     result *= x2;
  63. #endif
  64. #if (PRECISION >= 7)
  65.     result += 1./(1.*2*3*4*5*6*7*8*9*10*11*12*13*14);
  66.     result *= x2;
  67. #endif
  68. #if (PRECISION >= 6)
  69.     result += 1./(1.*2*3*4*5*6*7*8*9*10*11*12);
  70.     result *= x2;
  71. #endif
  72. #if (PRECISION >= 5)
  73.     result += 1./(1.*2*3*4*5*6*7*8*9*10);
  74.     result *= x2;
  75. #endif
  76.     result += 1./(1.*2*3*4*5*6*7*8);
  77.     result *= x2;
  78.  
  79.     result += 1./(1.*2*3*4*5*6);
  80.     result *= x2;
  81.  
  82.     result += 1./(1.*2*3*4);
  83.     result *= x2;
  84.  
  85.     result += 1./(1.*2);
  86.     result *= x2;
  87.  
  88.     result += 1;
  89.  
  90.     /* Apply correct sign */
  91.     result *= sin_sign_tbl[quadrant];
  92.  
  93.     return result;
  94. }


B#


Título: Re: [C++] Series de Taylor: sen(x)
Publicado por: Zeta255 en 24 Marzo 2021, 22:17 pm
lo he modificado con respecto a lo que me has dicho sinceramente no se que hacer ya, lo hago asi segun la formular pero me sigue dando nan no se porque no me calcula nada.



Código
  1. // Utilizar sumatoria taylor para calcular el valor de SEN con respecto a "X"
  2.  
  3. #include<iostream>
  4. #include <math.h>
  5. #include<iomanip>
  6.  
  7. using namespace std;
  8. int main(){
  9. double x, sen,numero, numerador=1, denominador=1,f=0;
  10.  
  11. cout<<"ingrese el valor de x para sen: "; cin>>x;
  12. for (int i=1; i<=360;i++)
  13. {
  14. sen=x*((i*i)+1);
  15. numero=(-1*i);
  16. numerador= numerador+(sen*numero);
  17. denominador=denominador+((i*i)+(1));
  18. f=f+(numerador/denominador);
  19. }
  20. cout<<"el valor de sen"<<x<<"= "<<f;
  21. }


Título: Re: [C++] Series de Taylor: sen(x)
Publicado por: K-YreX en 26 Marzo 2021, 22:36 pm
El problema de que te muestre nan como resultado es porque estás calculando números demasiado grandes. Realizar una serie de Taylor para el sen(x) con una precisión de 360 (como estás calculando tú) es una locura.
Llamando precisión al número de elementos de la serie que se calculan, tenemos lo siguiente:
Código:
Elemento 1: denominador = 1! = (1 * 2 - 1)!
Elemento 2: denominador = 3! = (2 * 2 - 1)!
Elemento 3: denominador = 5! = (3 * 2 - 1)!
Elemento 4: denominador = 7! = (4 * 2 - 1)!
...
Elemento 360: denominador = (360 * 2 - 1)! = 719!
Puedes intentar calcular 719! a ver qué pasa...

Una precisión de 10 es más que suficiente para una aproximación decente.
Dicho esto volvamos con el código en sí. La cosa es calcular cada uno de los elementos de la serie correctamente. Empezamos.
La estructura general del programa es la siguiente:
Código
  1. #include <iostream>
  2. #include <cmath>
  3.  
  4. using namespace std;
  5.  
  6. const int PRECISION = 10;
  7.  
  8. int main() {
  9.  double x = 2; // Un valor cualquiera para calcular sen(x)
  10.  
  11.  double resultado = 0;
  12.  for(int i = 0; i < PRECISION; ++i) {
  13.    resultado += signo * numerador / denominador;
  14.  }
  15.  
  16.  cout << "El resultado aproximado de sen(" << x << ") es: " << resultado << endl;
  17.  cout << "El resultado real de sen(" << x << ") es: " << sin(x) << endl;
  18. }
Ahora falta calcular cada una de las variables (signo, numerador, denominador).

Para calcular el signo, como ya te dije, basta con ir multiplicando por -1 en cada iteración.
Código
  1. //...
  2. int signo = 1; // Empezamos con 1 porque el primer elemento es positivo
  3. for(int i = 0; i < PRECISION; ++i) {
  4.  resultado += signo * numerador / denominador;
  5.  signo *= -1; // Cambiamos el signo a -1 para la siguiente iteracion
  6. }
  7. //...

Ahora para calcular el numerador, como ya dije también, hay que darse cuenta de que en cada iteración está elevado a un exponente 2 veces mayor.
Código:
Elemento 1: numerador = x^1 = x ^ (1 * 2 - 1)
Elemento 2: numerador = x^3 = x ^ (2 * 2 - 1)
Elemento 3: numerador = x^5 = x ^ (3 * 2 - 1)
...
La primera opción es calcular:
Código
  1. for(int i = 0; i < PRECISION; ++i) {
  2.  numerador = pow(x, i * 2 - 1);
  3.  //...
  4. }
Pero esto requiere demasiados cálculos innecesarios. Como ya dije:
Citar
Lo mismo pasa con una potencia: si tienes x y lo multiplicas por x, ya tienes x^2. Si x^2 lo multiplicas por x, ya tienes x^3 y así sucesivamente también.
Entonces la mejor opción es:
Código
  1. //...
  2. double numerador = x; // El primer numerador es x^1 entonces lo dejamos ya almacenado
  3. for(int i = 0; i < PRECISION; ++i) {
  4.  resultado += signo * numerador / denominador;
  5.  signo *= -1; // Cambiamos el signo a -1 para la siguiente iteracion
  6.  numerador *= (x * x); // Si tenemos x^1 y lo multiplicamos por x^2 (x*x), obtenemos x^3
  7. }
  8. //...

Y ya sólo quedaría el denominador. Vuelvo a mencionar lo que dije en el mensaje anterior:
Citar
Piensa que si en una iteración tienes 1! y lo multiplicas por 2, ya tienes 2!. Si 2! lo multiplicas por 3, ya tienes 3!; y así sucesivamente sin tener que hacer todas las operaciones en cada iteración.
Aquí también podríamos calcular el factorial de (2^n-1) pero volverían a ser un montón de cálculos innecesarios. Vamos a ver cómo funcionan los factoriales otra vez:
Código:
Elemento 1: denominador = 1! = (1 * 2 - 1)!
Elemento 2: denominador = 3! = (2 * 2 - 1)! = 1! * 2 * 3
Elemento 3: denominador = 5! = (3 * 2 - 1)! = 3! * 4 * 5
Elemento 4: denominador = 7! = (4 * 2 - 1)! = 5! * 6 * 7
...
Como ves, si en la primera iteración tenemos 1!, luego hay que multiplicar por 2 y por 3 para conseguir 3!. Para conseguir el 5! como ya tenemos 3! sólo hay que multiplicar por 4 y por 5 y así sucesivamente.
Código
  1. //...
  2. double denominador = 1; // El primer denominador es 1! (1) entonces lo dejamos ya almacenado
  3. for(int i = 0; i < PRECISION; ++i) {
  4.  resultado += signo * numerador / denominador;
  5.  signo *= -1; // Cambiamos el signo a -1 para la siguiente iteracion
  6.  numerador *= (x * x); // Si tenemos x^1 y lo multiplicamos por x^2 (x*x), obtenemos x^3
  7.  denominador *= ((i+2) * 2 - 2) * ((i+2) * 2 - 1);
  8. }
  9. //...
Tenemos que sumar 2 a i porque al empezar en 0, obtendríamos números negativos en las primeras iteraciones. Este valor que se suma a i dependerá del número (i) con el que se empiecen a contar las iteraciones.