简体   繁体   English

用 C 中的泰勒级数逼近 Sine(x) 并有很多问题

[英]Approximating Sine(x) with a Taylor series in C and having a lot of problems

I'm trying to approximate sine(x) in C using a Taylor series and Stirling's approximation for factorials but I'm getting really weird answers for n<5 and -0 for any n=>5.我正在尝试使用泰勒级数和斯特林对阶乘的近似来近似 C 中的正弦(x),但是对于任何 n=>5,对于 n<5 和 -0,我得到了非常奇怪的答案。 I literally just started learning yesterday so i'd appreciate it if some more experienced programmers could take a look at it and tell me what's wrong我昨天才刚刚开始学习,所以如果一些更有经验的程序员可以看看它并告诉我出了什么问题,我将不胜感激

Taylor Series of Sine泰勒级数的正弦

Stirling's approximation of factorials斯特林的阶乘近似

#include <stdio.h>
#include <math.h>

int main(){

float x,n,s,i,e,p,f,r;
f=M_PI;
e=2.7182818;
s=0;

printf("What value of sine do you want to apporximate?");
    scanf("%f", &x);
printf("With what level of precision do you want to calculate it?");
    scanf("%f", &n);
for(i=0;i<=n; ++i);{
    r=((2*i)+1);
    p=(sqrt(2*r*f)*(pow((r/e),r)));
    s=s+(((pow((-1),i))*(pow(x,((2*i)+1))))/p);
}
printf("the value of sine at %f is %f",x,s);
}

This line这条线

    for(i = 0; i <= n; ++i);{

has an extra semicolon.有一个额外的分号。 You're executing an empty loop.您正在执行一个空循环。

BAsed on your formulat this is the correct code but it generate wrong output so you need to check again your formulat:根据您的公式,这是正确的代码,但它会生成错误的输出,因此您需要再次检查您的公式:

#include <stdio.h>
#include <math.h>

int main(){

double x,n,s,i,e,p,f;
f=M_PI;
e=2.7182818;
s=0;
int sign=0;// Adding this type to toggle the sign 

printf("What value of sine do you want to apporximate?");
    scanf("%lf", &x);// conversion specifier must be %lf for floating number
printf("With what level of precision do you want to calculate it?");
    scanf("%lf", &n);
for(i=1;i<=n; i=i+2){ // Correcting the for loop
    p=sqrt(2*i*f)*pow((i/e),i); 
    s=s+(pow(-1,sign++)*pow(x,i))/p;
}
printf("the value of sine at %f is %f",x,s);
}

this is easier bro这更容易兄弟

#include <iostream>
#include <cmath>
using namespace std;
double factorial(int X)
{
    double factorial = 1;
    for(int i=1; i<=X; i++)
    {
        factorial = factorial *i;
    }
    return factorial;
}
double Mysin(double x,double result)
{
        for(int i = 0;i<20;i++)
    {
        result+=pow((-1),i)*pow(x,((2*i)+1))/factorial((2*i)+1);
    }
    return result;
}
double Mycos(double x,double result)
{
    for(int i = 0;i<20;i++)
    {
        result+=pow(-1,i)*pow(x,2*i)/factorial(2*i);
    }
    return result;
}
double Mytan(double sine,double cosine)
{
    return sine/cosine;
}
double deg_to_rad(double x)
{
    double const pi = 3.14159265359;
    return x*pi/180;
}
int main()
{
    double x,result=0;
    cin>>x;
    cout<<"My sin: "<<Mysin(deg_to_rad(x),result)<<endl;
    cout<<"My cosin: "<<Mycos(deg_to_rad(x),result)<<endl;
    cout<<"My tan: "<<Mytan(Mysin(deg_to_rad(x),result),Mycos(deg_to_rad(x),result))<<endl;
    return 0;
}

Check your factorial function: it returns a double, but you will see that factorial of numbers like 8 or 10 are beyond the range of a double variable.检查你的阶乘函数:它返回一个双精度,但你会看到像 8 或 10 这样的数字的阶乘超出了双精度变量的范围。 Use long as returning type...使用 long 作为返回类型...

As the sin() is a periodic function, I should never go further than one period to calculate it.由于sin()是一个周期函数,我不应该超过一个周期来计算它。 This simplifies too much mathematics, as you never need to calculate great factorial numbers.这简化了太多的数学运算,因为您永远不需要计算很大的阶乘数。 Indeed you even don't need to calculate the factorial for each term in the series, as the coefficients can be derived one from the last, by just dividing the previous coefficient by (n-1) and n .实际上,您甚至不需要计算系列中每个项的阶乘,因为系数可以从最后一项导出,只需将前一个系数除以(n-1)n即可。 if your input is bounded to one period (well, you don't need to use a fixed period of M_PI , you can go let's say to a maximum value of 3.5 and reduce your answers for values greater by just reducing by the modulus of the division by M_PI .如果您的输入仅限于一个周期(好吧,您不需要使用M_PI的固定周期,您可以假设最大值为3.5并通过仅减少模数来减少更大的值的答案除以M_PI

Once said this, we can bind your maximum error, as for the greatest input of 3.5 we will have 3.5^n/n!说到这里,我们可以绑定你的最大误差,至于最大输入3.5我们将有3.5^n/n! as the last term of our approximation, with is bounded for some n to be less than some maximum error which fixes the number of terms we'll need to calculate.作为我们近似值的最后一项,with 是有界的,一些n小于某个最大误差,它固定了我们需要计算的项数。

Instead of trying to be precise with the number of terms needed to calculate, I'll try to do some guesses, from deriving an algorithm and showing actual values (for example, for the maximum input value of 3.2 )我不会尝试精确计算所需的项数,而是尝试通过推导算法和显示实际值进行一些猜测(例如,对于3.2的最大输入值)

These are the values of the term at position n for input of 3.2 and这些是输入3.2和位置n处的项的值

   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 we can stop on calculating just 20 terms of the series.所以我们可以停止计算该系列的 20 项。 This is true for the exp() function which has all terms added and is a simple function.对于添加了所有术语并且是一个简单函数的exp()函数来说也是如此。 For the sin() or cos() you can guess that a better error estimate if you consider that both have the same terms of the exp() function, (well the first has only the odd terms, the second has only the even terms)对于sin()cos() ,如果您认为两者都具有exp()函数的相同项,您可以猜测出更好的误差估计,(第一个只有奇数项,第二个只有偶数项)

 (x^n)/(n!) - (x^(n+2))/((n+2)!) = (n!*x^n*(1 - x^2/((n+1)*(n+2))))/n!

that for n > 3.2 means each term is对于n > 3.2意味着每个术语是

< x^n/n!

so we can apply the same criterion as for the exponential.所以我们可以应用与指数相同的标准。

This to say that we can stop at some point... if we continue our table we'll see that at, for example n > 30 the overall accumulated term is less than 5.3E-18 so we can stop there (for a double number, at least).这就是说我们可以在某个点停下来......如果我们继续我们的表格,我们会看到,例如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));
    }
}

If you take advantage from the symmetries that the sin function has, you can reduce your domain to M_PI/4 which is less than one, and you can stop even at term of power 18 to get around 17 significative digits (for a double ) which makes your sin faster.如果您利用 sin 函数具有的对称性,您可以将域减少到小于 1 的M_PI/4 ,甚至可以在 18 次幂时停止以获得大约 17 个有效数字(对于double ),使你的罪更快。

Finally, we can get a valid for all real domain sin2() function by:最后,我们可以通过以下方式获得对所有实域均有效的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);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM