在 C 中实现正弦和余弦的泰勒级数
Implementing Taylor Series for sine and cosine in C
我一直在按照我的教授给我们的指南进行操作,但我就是找不到哪里出错了。我还一直在研究有关在 C 中实现泰勒级数的其他一些问题。
假设存在 RaiseTo(计算一个数的 x 次方)。
double factorial (int n)
{
int fact = 1,
flag;
for (flag = 1; flag <= n; flag++)
{
fact *= flag;
}
return flag;
}
double sine (double rad)
{
int flag_2,
plusOrMinus2 = 0; //1 for plus, 0 for minus
double sin,
val2 = rad,
radRaisedToX2,
terms;
terms = NUMBER_OF_TERMS; //10 terms
for (flag_2 = 1; flag_2 <= 2 * terms; flag_2 += 2)
{
radRaisedToX2 = RaiseTo(rad, flag_2);
if (plusOrMinus2 == 0)
{
val2 -= radRaisedToX2/factorial(flag_2);
plusOrMinus2++; //Add the next number
}
else
{
val2 += radRaisedToX2/factorial(flag_2);
plusOrMinus2--; //Subtract the next number
}
}
sin = val2;
return sin;
}
int main()
{
int degree;
scanf("%d", °ree);
double rad, cosx, sinx;
rad = degree * PI / 180.00;
//cosx = cosine (rad);
sinx = sine (rad);
printf("%lf \n%lf", rad, sinx);
}
所以在循环中,我得到 rad^x,将它除以从 1 开始的奇数级数的阶乘,然后根据需要加减它,但是当我 运行程序,我得到的输出远高于 1,我们都知道 sin(x) 的极限是 1 和 -1,我真的很想知道我哪里出错了,这样我就可以改进,抱歉,如果这是一个非常糟糕的问题.
在函数 factorial
中,您在分配给函数的 double
return 值之前进行 int
乘法运算。阶乘很容易打破 int
范围,例如 20! = 2432902008176640000
.
您还return输入了错误的变量 - 循环计数器!
请将局部变量改为double
,如
double factorial (int n)
{
double fact = 1;
int flag;
for (flag = 1; flag <= n; flag++)
{
fact *= flag;
}
return fact; // it was the wrong variable, and wrong type
}
此外,甚至不需要阶乘计算。请注意,该系列的每一项都将前一项乘以 rad
并除以项号 - 符号发生变化。
任何超过 12!
的值都大于 32 位 int
可以容纳的值,因此这些值会溢出,因此不会 return 您所期望的。
与其每次都计算完整的阶乘,不如看看序列中的每一项相对于前一项的情况。对于任何给定的术语,下一个是前一个的 -((x*x)/(flag_2*(flag_2-1))
倍。因此,从 x
的项开始,然后为每个连续的项乘以该因子。
还有一个技巧可以在不知道需要多少项的情况下将结果计算到 double
的精度。我将把它留给 reader.
作为练习
另一种相当幼稚的 5 分钟方法涉及计算包含前 20 个左右阶乘的查找 table,即 1! .. 20!这需要很少的内存,并且可以提高 'each-time' 计算方法的速度。进一步的优化可以很容易地在预先计算阶乘的函数中实现,利用每个阶乘与前一个阶乘的关系。
在两个三角函数的循环中有效消除分支(如果 X 执行 Y 否则执行 Z)的方法将再次提供更快的速度。
C代码
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
const int nMaxTerms=20;
double factorials[nMaxTerms];
double factorial(int n)
{
if (n==1)
return 1;
else
return (double)n * factorial(n - 1.0);
}
void precalcFactorials()
{
for (int i=1; i<nMaxTerms+1; i++)
{
factorials[i-1] = factorial(i);
}
}
/*
sin(x) = x - (x^3)/3! + (x^5)/5! - (x^7)/7! .......
*/
double taylorSine(double rads)
{
double result = rads;
for (int curTerm=1; curTerm<=(nMaxTerms/2)-1; curTerm++)
{
double curTermValue = pow(rads, (curTerm*2)+1);
curTermValue /= factorials[ curTerm*2 ];
if (curTerm & 0x01)
result -= curTermValue;
else
result += curTermValue;
}
return result;
}
/*
cos(x) = 1 - (x^2)/2! + (x^4)/4! - (x^6)/6! .......
*/
double taylorCos(double rads)
{
double result = 1.0;
for (int curTerm=1; curTerm<=(nMaxTerms/2)-1; curTerm++)
{
double curTermValue = pow(rads, (curTerm*2) );
curTermValue /= factorials[ (curTerm*2) - 1 ];
if (curTerm & 0x01)
result -= curTermValue;
else
result += curTermValue;
}
return result;
}
int main()
{
precalcFactorials();
printf("Math sin(0.5) = %f\n", sin(0.5));
printf("taylorSin(0.5) = %f\n", taylorSine(0.5));
printf("Math cos(0.5) = %f\n", cos(0.5));
printf("taylorCos(0.5) = %f\n", taylorCos(0.5));
return 0;
}
输出
Math sin(0.5) = 0.479426
taylorSin(0.5) = 0.479426
Math cos(0.5) = 0.877583
taylorCos(0.5) = 0.877583
Javascript
在 javascript 中实现,当对 sin/cos 函数中的 7 个项求和时,代码产生与内置数学库看似相同的结果(我没有进行太多测试)。
window.addEventListener('load', onDocLoaded, false);
function onDocLoaded(evt)
{
console.log('starting');
for (var i=1; i<21; i++)
factorials[i-1] = factorial(i);
console.log('calculated');
console.log(" Math.cos(0.5) = " + Math.cos(0.5));
console.log("taylorCos(0.5) = " + taylorCos(0.5));
console.log('-');
console.log(" Math.sin(0.5) = " + Math.sin(0.5));
console.log("taylorSine(0.5) = " + taylorSine(0.5));
}
var factorials = [];
function factorial(n)
{
if (n==1)
return 1;
else
return n * factorial(n-1);
}
/*
sin(x) = x - (x^3)/3! + (x^5)/5! - (x^7)/7! .......
*/
function taylorSine(x)
{
var result = x;
for (var curTerm=1; curTerm<=7; curTerm++)
{
var curTermValue = Math.pow(x, (curTerm*2)+1);
curTermValue /= factorials[ curTerm*2 ];
if (curTerm & 0x01)
result -= curTermValue;
else
result += curTermValue;
}
return result;
}
/*
cos(x) = 1 - (x^2)/2! + (x^4)/4! - (x^6)/6! .......
*/
function taylorCos(x)
{
var result = 1.0;
for (var curTerm=1; curTerm<=7; curTerm++)
{
var curTermValue = Math.pow(x, (curTerm*2));
curTermValue /= factorials[ (curTerm*2)-1 ];
if (curTerm & 0x01)
result -= curTermValue;
else
result += curTermValue;
}
return result;
}
我一直在按照我的教授给我们的指南进行操作,但我就是找不到哪里出错了。我还一直在研究有关在 C 中实现泰勒级数的其他一些问题。
假设存在 RaiseTo(计算一个数的 x 次方)。
double factorial (int n)
{
int fact = 1,
flag;
for (flag = 1; flag <= n; flag++)
{
fact *= flag;
}
return flag;
}
double sine (double rad)
{
int flag_2,
plusOrMinus2 = 0; //1 for plus, 0 for minus
double sin,
val2 = rad,
radRaisedToX2,
terms;
terms = NUMBER_OF_TERMS; //10 terms
for (flag_2 = 1; flag_2 <= 2 * terms; flag_2 += 2)
{
radRaisedToX2 = RaiseTo(rad, flag_2);
if (plusOrMinus2 == 0)
{
val2 -= radRaisedToX2/factorial(flag_2);
plusOrMinus2++; //Add the next number
}
else
{
val2 += radRaisedToX2/factorial(flag_2);
plusOrMinus2--; //Subtract the next number
}
}
sin = val2;
return sin;
}
int main()
{
int degree;
scanf("%d", °ree);
double rad, cosx, sinx;
rad = degree * PI / 180.00;
//cosx = cosine (rad);
sinx = sine (rad);
printf("%lf \n%lf", rad, sinx);
}
所以在循环中,我得到 rad^x,将它除以从 1 开始的奇数级数的阶乘,然后根据需要加减它,但是当我 运行程序,我得到的输出远高于 1,我们都知道 sin(x) 的极限是 1 和 -1,我真的很想知道我哪里出错了,这样我就可以改进,抱歉,如果这是一个非常糟糕的问题.
在函数 factorial
中,您在分配给函数的 double
return 值之前进行 int
乘法运算。阶乘很容易打破 int
范围,例如 20! = 2432902008176640000
.
您还return输入了错误的变量 - 循环计数器!
请将局部变量改为double
,如
double factorial (int n)
{
double fact = 1;
int flag;
for (flag = 1; flag <= n; flag++)
{
fact *= flag;
}
return fact; // it was the wrong variable, and wrong type
}
此外,甚至不需要阶乘计算。请注意,该系列的每一项都将前一项乘以 rad
并除以项号 - 符号发生变化。
任何超过 12!
的值都大于 32 位 int
可以容纳的值,因此这些值会溢出,因此不会 return 您所期望的。
与其每次都计算完整的阶乘,不如看看序列中的每一项相对于前一项的情况。对于任何给定的术语,下一个是前一个的 -((x*x)/(flag_2*(flag_2-1))
倍。因此,从 x
的项开始,然后为每个连续的项乘以该因子。
还有一个技巧可以在不知道需要多少项的情况下将结果计算到 double
的精度。我将把它留给 reader.
另一种相当幼稚的 5 分钟方法涉及计算包含前 20 个左右阶乘的查找 table,即 1! .. 20!这需要很少的内存,并且可以提高 'each-time' 计算方法的速度。进一步的优化可以很容易地在预先计算阶乘的函数中实现,利用每个阶乘与前一个阶乘的关系。
在两个三角函数的循环中有效消除分支(如果 X 执行 Y 否则执行 Z)的方法将再次提供更快的速度。
C代码
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
const int nMaxTerms=20;
double factorials[nMaxTerms];
double factorial(int n)
{
if (n==1)
return 1;
else
return (double)n * factorial(n - 1.0);
}
void precalcFactorials()
{
for (int i=1; i<nMaxTerms+1; i++)
{
factorials[i-1] = factorial(i);
}
}
/*
sin(x) = x - (x^3)/3! + (x^5)/5! - (x^7)/7! .......
*/
double taylorSine(double rads)
{
double result = rads;
for (int curTerm=1; curTerm<=(nMaxTerms/2)-1; curTerm++)
{
double curTermValue = pow(rads, (curTerm*2)+1);
curTermValue /= factorials[ curTerm*2 ];
if (curTerm & 0x01)
result -= curTermValue;
else
result += curTermValue;
}
return result;
}
/*
cos(x) = 1 - (x^2)/2! + (x^4)/4! - (x^6)/6! .......
*/
double taylorCos(double rads)
{
double result = 1.0;
for (int curTerm=1; curTerm<=(nMaxTerms/2)-1; curTerm++)
{
double curTermValue = pow(rads, (curTerm*2) );
curTermValue /= factorials[ (curTerm*2) - 1 ];
if (curTerm & 0x01)
result -= curTermValue;
else
result += curTermValue;
}
return result;
}
int main()
{
precalcFactorials();
printf("Math sin(0.5) = %f\n", sin(0.5));
printf("taylorSin(0.5) = %f\n", taylorSine(0.5));
printf("Math cos(0.5) = %f\n", cos(0.5));
printf("taylorCos(0.5) = %f\n", taylorCos(0.5));
return 0;
}
输出
Math sin(0.5) = 0.479426
taylorSin(0.5) = 0.479426
Math cos(0.5) = 0.877583
taylorCos(0.5) = 0.877583
Javascript
在 javascript 中实现,当对 sin/cos 函数中的 7 个项求和时,代码产生与内置数学库看似相同的结果(我没有进行太多测试)。
window.addEventListener('load', onDocLoaded, false);
function onDocLoaded(evt)
{
console.log('starting');
for (var i=1; i<21; i++)
factorials[i-1] = factorial(i);
console.log('calculated');
console.log(" Math.cos(0.5) = " + Math.cos(0.5));
console.log("taylorCos(0.5) = " + taylorCos(0.5));
console.log('-');
console.log(" Math.sin(0.5) = " + Math.sin(0.5));
console.log("taylorSine(0.5) = " + taylorSine(0.5));
}
var factorials = [];
function factorial(n)
{
if (n==1)
return 1;
else
return n * factorial(n-1);
}
/*
sin(x) = x - (x^3)/3! + (x^5)/5! - (x^7)/7! .......
*/
function taylorSine(x)
{
var result = x;
for (var curTerm=1; curTerm<=7; curTerm++)
{
var curTermValue = Math.pow(x, (curTerm*2)+1);
curTermValue /= factorials[ curTerm*2 ];
if (curTerm & 0x01)
result -= curTermValue;
else
result += curTermValue;
}
return result;
}
/*
cos(x) = 1 - (x^2)/2! + (x^4)/4! - (x^6)/6! .......
*/
function taylorCos(x)
{
var result = 1.0;
for (var curTerm=1; curTerm<=7; curTerm++)
{
var curTermValue = Math.pow(x, (curTerm*2));
curTermValue /= factorials[ (curTerm*2)-1 ];
if (curTerm & 0x01)
result -= curTermValue;
else
result += curTermValue;
}
return result;
}