Als sin()
eine periodische Funktion ist, sollte ich nie weiter gehen als eine Periode um es zu berechnen. Dies vereinfacht zu viel Mathematik, da Sie nie große faktorielle Zahlen berechnen müssen. In der Tat müssen Sie den Faktor für jeden Ausdruck in der Reihe sogar nicht berechnen, da die Koeffizienten vom letzten abgeleitet werden können, indem Sie nur den vorhergehenden Koeffizienten durch (n-1)
und n
teilen. Wenn Ihre Eingabe auf einen Zeitraum beschränkt ist (Sie müssen also keinen festen Zeitraum von M_PI
verwenden, können Sie sagen, dass Sie einen maximalen Wert von 3.5
haben und Ihre Antworten auf Werte, die größer sind, reduzieren, indem Sie einfach den Division durch M_PI
.
sagte Sobald dies, wir Ihre maximale Fehler binden können, wie für den größten Eingang 3.5
wir 3.5^n/n!
als letzter Ausdruck unserer Annäherung haben, mit für einige n
begrenzt ist, einige maximal auf weniger als Fehler, der die Anzahl der zu berechnenden Terme festlegt
Anstatt zu versuchen, mit der Anzahl der zu berechnenden Terme genau zu sein, werde ich versuchen, einige Vermutungen zu machen Aus einem Algorithmus abgeleitet und Istwerten (zum Beispiel für den maximalen Eingangswert von 3.2
)
Dies sind die Werte der Laufzeit an der Position n
zur Eingabe von 3.2
und
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
So können zeigen wir Hören Sie auf, nur 20 Begriffe der Serie zu berechnen. Dies gilt für die exp()
-Funktion, die alle Begriffe hinzugefügt hat und eine einfache Funktion ist.Für die sin()
oder cos()
Sie, dass eine bessere Fehlerabschätzung erraten können, wenn man bedenkt, dass beide die gleichen Bedingungen der exp()
Funktion haben, (auch die erste hat nur die ungeraden Terme hat der zweite nur die geraden Terme)
(x^n)/(n!) - (x^(n+2))/((n+2)!) = (n!*x^n*(1 - x^2/((n+1)*(n+2))))/n!
, die für jeden Begriff n > 3.2
Mittel
< x^n/n!
so können wir das gleiche Kriterium wie für die exponentielle anzuwenden.
Dies sagen, dass wir irgendwann aufhören können ... wenn wir unseren Tisch wir weiterhin an, dass sehen werden, zum Beispiel n > 30
die gesamte kumulierte Laufzeit beträgt weniger als 5.3E-18
so können wir dort nicht mehr (für eine double
Nummer , mindestens).
#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));
}
}
Wenn Sie die Vorteile von den Symmetrien nehmen, dass die sin-Funktion verfügt, können Sie Ihre Domain zu M_PI/4
reduzieren, die kleiner als eins ist, und Sie können auch bei Laufzeit von Macht 18 erhalten rund 17 significative Ziffern stoppen (für eine double
), die Ihre Sünde schneller macht.
Schließlich können wir eine gültige für alle reellen Domain sin2()
Funktion erhalten:
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);
}
sollten Sie 'double's verwenden anstelle von' float's und 'I' und 'n' sollte 'int's. –
Es gibt auch wenig Notwendigkeit, die Fakultät hier anzunähern. Berechnen Sie jeden Ausdruck stattdessen inkremental, dh indem Sie nacheinander eine Termvariable mit ((-x * x)/(i * (i + 1)) 'jeder Iteration multiplizieren. – doynax