Comme le sin()
est une fonction périodique, je n'aller plus loin d'une période pour le calculer. Cela simplifie trop les mathématiques, car vous n'avez jamais besoin de calculer de grands nombres factoriels. En effet, vous n'avez même pas besoin de calculer la factorielle pour chaque terme de la série, car les coefficients peuvent être déduits l'un de l'autre, en divisant simplement le coefficient précédent par (n-1)
et n
. si votre entrée est limitée à une période (eh bien, vous n'avez pas besoin d'utiliser une période fixe de M_PI
, vous pouvez aller jusqu'à une valeur maximale de 3.5
et réduire vos réponses pour les valeurs plus grandes en réduisant juste le module de division par M_PI
.
une fois dit cela, nous pouvons lier votre erreur maximale, comme pour la plus grande entrée de 3.5
nous aurons 3.5^n/n!
comme le dernier terme de notre rapprochement, avec est limitée pour une n
être inférieure à une maximale erreur qui fixe le nombre de termes que nous aurons besoin de calculer
Au lieu d'essayer d'être précis avec le nombre de termes nécessaires pour calculer, je vais essayer de faire quelques suppositions , De déduire un algorithme et montrant des valeurs réelles (par exemple, pour la valeur d'entrée maximale de 3.2
)
Ce sont les valeurs du terme à la position n
pour l'entrée de 3.2
et
n | term at position n for input `3.2`
======+=================
8 | 0.27269634
12 | 0.00240693
16 | 0.00000578
18 | 0.00000019
20 | 0.00000001
21 | 7.9E-10
Nous pouvons donc arrêtez de calculer seulement 20 termes de la série. Cela est vrai pour la fonction exp()
qui a tous les termes ajoutés et est une fonction simple.Pour le sin()
ou cos()
vous pouvez deviner qu'une meilleure estimation d'erreur si l'on considère que les deux ont les mêmes termes de la fonction exp()
, (bien le premier n'a que les termes impairs, le second n'a que les même termes)
(x^n)/(n!) - (x^(n+2))/((n+2)!) = (n!*x^n*(1 - x^2/((n+1)*(n+2))))/n!
que pour n > 3.2
signifie que chaque terme est
< x^n/n!
donc nous pouvons appliquer le même critère que pour l'exponentielle.
Ceci pour dire que nous pouvons arrêter à un moment donné ... si nous continuons notre table, nous verrons que, par exemple à n > 30
le terme global cumulé est inférieur à 5.3E-18
afin que nous puissions arrêter là (pour un certain nombre double
, au moins).
#include <stdio.h>
#include <math.h> /* for the system sin() function */
double MySin(double x) /* x must be in the range [0..3.2] */
{
int i;
const int n = 30;
double t = x, acum = x; /* first term, x/1! */
x *= x; /* square the argument so we get x^2 in variable x */
for (i = 3; i < n; i += 2) {
t = -t * x/i/(i-1); /* mutiply by -1, x^2 and divide by i and (i-1) */
acum += t; /* and add it to the accum */
}
return acum;
}
int main()
{
double arg;
for(;;) {
if (scanf("%lg", &arg) != 1)
break;
printf("MySin(%lg) = %lg; sin(%lg) = %lg\n",
arg, MySin(arg), arg, sin(arg));
}
}
Si vous profitez des symétries que la fonction sin a, vous pouvez réduire votre domaine à M_PI/4
qui est inférieur à un, et vous pouvez arrêter même à terme de puissance 18 pour obtenir environ 17 chiffres significatifs (pour un double
) qui rend votre péché plus rapide.
Enfin, nous pouvons obtenir une valable pour tous les biens domaine sin2()
fonction par:
double sin2(double x)
{
bool neg = false;
int ip = x/2.0/M_PI;
x -= 2.0 * M_PI * ip; /* reduce to first period [-2PI..2PI] */
if (x < 0.0) x += 2.0*M_PI; /* reduce to first period [0..2PI] */
if (x > M_PI) { x -= M_PI; neg = true; } /* ... first period negative [ 0..PI ] */
if (x > M_PI/2.0) x = M_PI - x; /* reflection [0..PI/2] */
return neg ? MySin(-x) : MySin(x);
}
Vous devez utiliser 'double's au lieu de' float's et 'I' et' n' devrait être 'int's. –
Il y a aussi peu besoin d'approximer la factorielle ici. Calculer chaque terme de façon incrémentielle à la place, c'est-à-dire en multipliant successivement une variable terme par '(-x * x)/(i * (i + 1))' à chaque itération. – doynax