我有一个程序可以根据泰勒系列计算arcsin值的近似值。
我和我的朋友想出了一个算法,该算法能够返回差不多的"右键"价值观,但我不认为我们已经非常清晰地完成了它。看看:
double my_asin(double x)
{
double a = 0;
int i = 0;
double sum = 0;
a = x;
for(i = 1; i < 23500; i++)
{
sum += a;
a = next(a, x, i);
}
}
double next(double a, double x, int i)
{
return a*((my_pow(2*i-1, 2)) / ((2*i)*(2*i+1)*my_pow(x, 2)));
}
我检查了my_pow是否正常工作,所以我也不需要在这里发布它。基本上我想要在当前和下一个术语之间的差异大于或等于我的EPSILON(0.00001)时结束循环,这是我在计算平方根时所使用的精度。
这就是我希望它的工作方式:
while(my_abs(prev_term - next_term) >= EPSILON)
但是函数 double next 依赖于 i ,所以我想我也必须在while语句中增加它。有什么想法我应该怎么做呢?
-1的输出示例:
$ -1.5675516116e+00
而不是:
$ -1.5707963268e+00
非常感谢你们。
答案 0 :(得分:3)
您的代码和问题的问题包括:
a*((my_pow(2*i-1, 2)) / ((2*i)*(2*i+1)*my_pow(x, 2)))
在每个字词中按 x 2 划分。这与您询问的特定值-1无关,但除了1之外,它会对其他值产生错误的结果。a
,这样您就不会同时拥有上一个词和下一个词。而是创建另一个double b
,以便您拥有前一个术语的对象b
和当前术语的对象a
,如下所示。示例:
double a = x, b, sum = a;
int i = 0;
do
{
b = a;
a = next(a, x, ++i);
sum += a;
} while (abs(b-a) > threshold);
答案 1 :(得分:1)
所以我想我也必须在while语句中增加它
是的,这可能是一种方式。是什么阻止了你?
int i=0;
while(condition){
//do something
i++;
}
另一种方法是使用for条件:
for(i = 1; i < 23500 && my_abs(prev_term - next_term) >= EPSILON; i++)
答案 2 :(得分:1)
你的公式错了。这是正确的公式:http://scipp.ucsc.edu/~haber/ph116A/taylor11.pdf。
P.S。另请注意,您的公式和系列彼此不对应。
您可以像这样使用:
while( std::abs(sum_prev - sum) < 1e-15 )
{
sum_prev = sum;
sum += a;
a = next(a, x, i);
}
答案 3 :(得分:1)
arcsin
使用泰勒系列是非常不精确的,因为这些东西收敛得非常糟糕,并且对于有限数量的温度,真实的东西会有相对较大的差异。使用带有整数指数的pow
也不是非常精确和有效。
然而,使用arctan
就可以了
arcsin(x) = arctan(x/sqrt(1-(x*x)));
因为它的泰勒级数在<0.0,0.8>
范围内收敛,所以该范围的所有其他部分都可以通过它计算(使用三角恒等式)。所以我的 C ++ 实现(来自我的算术模板):
T atan (const T &x) // = atan(x)
{
bool _shift=false;
bool _invert=false;
bool _negative=false;
T z,dz,x1,x2,a,b; int i;
x1=x; if (x1<0.0) { _negative=true; x1=-x1; }
if (x1>1.0) { _invert=true; x1=1.0/x1; }
if (x1>0.7) { _shift=true; b=::sqrt(3.0)/3.0; x1=(x1-b)/(1.0+(x1*b)); }
x2=x1*x1;
for (z=x1,a=x1,b=1,i=1;i<1000;i++) // if x1>0.8 convergence is slow
{
a*=x2; b+=2; dz=a/b; z-=dz;
a*=x2; b+=2; dz=a/b; z+=dz;
if (::abs(dz)<zero) break;
}
if (_shift) z+=pi/6.0;
if (_invert) z=0.5*pi-z;
if (_negative) z=-z;
return z;
}
T asin (const T &x) // = asin(x)
{
if (x<=-1.0) return -0.5*pi;
if (x>=+1.0) return +0.5*pi;
return ::atan(x/::sqrt(1.0-(x*x)));
}
T
是任何浮点类型(float,double
,...)。如您所见,您需要执行sqrt(x)
,pi=3.141592653589793238462643383279502884197169399375105
,zero=1e-20
和+,-,*,/
操作。 zero
常量是目标精度。
所以只需将T
替换为float/double
,然后忽略::
...