Как sin()
является периодической функцией, я никогда не должны идти дальше, чем один период для его расчета. Это упрощает слишком много математики, так как вам никогда не нужно вычислять большие числовые числа. Действительно, вам даже не нужно вычислять факториал для каждого члена в серии, так как коэффициенты могут быть получены от последнего, просто разделив предыдущий коэффициент на (n-1)
и n
. если ваш ввод ограничен одним периодом (ну, вам не нужно использовать фиксированный период M_PI
, вы можете пойти, допустим, к максимальному значению 3.5
и уменьшить ваши ответы для значений больше, просто уменьшив модуль деление на M_PI
.
После того, как сказал это, мы можем связать максимальную ошибку, так как для наибольшего ввода 3.5
мы будем иметь 3.5^n/n!
в качестве последнего члена нашего приближения, с ограничен для некоторых n
быть меньше некоторого максимума ошибка, которая фиксирует количество терминов, которые нам нужно вычислить.
Вместо того, чтобы быть точным с количеством терминов, необходимых для расчета, я попытаюсь сделать некоторые догадки От вывода алгоритма и показаны фактические значения (например, для максимального значения входного сигнала 3.2
)
Эти значения термина в положении n
для ввода 3.2
и
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
Таким образом, мы можем остановка при вычислении всего 20 членов серии. Это верно для функции exp()
, которая добавляет все термины и является простой функцией.Для sin()
или cos()
вы можете догадаться, что лучшая оценка погрешности, если учесть, что оба имеют одинаковые условия функции exp()
, (ну первое имеет только нечетные член, то второй имеют только четные термины)
(x^n)/(n!) - (x^(n+2))/((n+2)!) = (n!*x^n*(1 - x^2/((n+1)*(n+2))))/n!
что для n > 3.2
означает, что каждый член является
< x^n/n!
так что мы можем применить тот же критерий, что и для экспоненциальной.
Это означает, что мы можем остановиться на какой-то момент ... если мы продолжим нашу таблицу, мы увидим, что при, например, n > 30
общий накопленный срок меньше 5.3E-18
так что мы можем там остановиться (для double
числа , как минимум).
#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));
}
}
Если вы воспользоваться из симметрий, что функция греха, вы можете уменьшить свой домен M_PI/4
, который меньше, чем один, и вы можете остановить даже при сроке власти 18, чтобы получить около 17 сигнификативных цифр (для double
), что делает ваш грех быстрее.
Наконец, мы можем получить действительный для всех действительных функций домена sin2()
по:
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);
}
Вы должны использовать 'double's вместо' float's и 'i' и' n' должны быть 'int's. –
Здесь также мало нужно приближать факториал. Вычисляйте каждый термин поэтапно вместо этого, то есть путем последовательного умножения переменной терма на '(-x * x)/(i * (i + 1))' каждая итерация. – doynax