在 C# 中这个反正弦近似的实现有什么问题
What wrong with this implement of this arcsine approximate in C#
这是使用来自 this blog 的泰勒级数近似反正弦 (x) 的公式
这是我用C#实现的,我不知道哪里错了,代码在运行时给出了错误的结果:
当 i = 0 时,除法将为 1/x。所以我在启动时分配 temp = 1/x 。对于每次迭代,我在 "i" 之后更改 "temp"。
我使用连续循环,直到两个下一个值非常 "near" 在一起。当下两个数的差值很小的时候,我会return这个值。
我的测试用例:
输入是 x =1,所以 excected arcsin(X) 将是 arcsin (1) = PI/2 = 1.57079633 rad.
class Arc{
static double abs(double x)
{
return x >= 0 ? x : -x;
}
static double pow(double mu, long n)
{
double kq = mu;
for(long i = 2; i<= n; i++)
{
kq *= mu;
}
return kq;
}
static long fact(long n)
{
long gt = 1;
for (long i = 2; i <= n; i++) {
gt *= i;
}
return gt;
}
#region arcsin
static double arcsinX(double x) {
int i = 0;
double temp = 0;
while (true)
{
//i++;
var iFactSquare = fact(i) * fact(i);
var tempNew = (double)fact(2 * i) / (pow(4, i) * iFactSquare * (2*i+1)) * pow(x, 2 * i + 1) ;
if (abs(tempNew - temp) < 0.00000001)
{
return tempNew;
}
temp = tempNew;
i++;
}
}
public static void Main(){
Console.WriteLine(arcsin());
Console.ReadLine();
}
}
注意: 我将此作为社区维基答案,因为我不是第一个想到这个的人(只是第一个在评论中写下它的人)。如果您觉得需要添加更多内容才能使答案完整,请在其中进行编辑!
普遍怀疑这是因为 Integer Overflow,即您的其中一个值(可能是 fact()
或 iFactSquare()
的 return)变得太大了对于您选择的类型。它会变成负数,因为你使用的是有符号类型——当它变成一个太大的正数时,它会循环回到负数。
尝试跟踪计算过程中 n
的大小,并计算出如果通过 fact
、[=14] 运行 这个数字,它会给你多大的数字=] 和 iFactSquare
函数。如果它大于 Maximum long value in 64-bit like we think (assuming you're using 64-bit, it'll be a lot smaller for 32-bit), then try using a double instead.
在很多series evaluation中,经常使用term之间的商来更新term会很方便。这里的商是
(2n)!*x^(2n+1) 4^(n-1)*((n-1)!)^2*(2n-1)
a[n]/a[n-1] = ------------------- * --------------------- -------
(4^n*(n!)^2*(2n+1)) (2n-2)!*x^(2n-1)
=(2n(2n-1)²x²)/(4n²(2n+1))
= ((2n-1)²x²)/(2n(2n+1))
因此计算系列值的循环是
sum = 1;
term = 1;
n=1;
while(1 != 1+term) {
term *= (n-0.5)*(n-0.5)*x*x/(n*(n+0.5));
sum += term;
n += 1;
}
return x*sum;
仅保证 abs(x)<1
的收敛,对于 x=1
处的评估,您必须使用角度减半,这通常是加速收敛的好主意。
您正在保存两个不同的临时值(temp 和 tempNew)以检查继续计算是否无关紧要。这很好,只是您没有保存这两个值的总和。
这是总结。您需要将每个新的计算值添加到总数中。您只跟踪最近计算的值。您只能 return 系列的最后一个计算值。所以你的结果总是会得到一个非常小的数字。将其转化为求和,问题就会消失。
这是使用来自 this blog 的泰勒级数近似反正弦 (x) 的公式
这是我用C#实现的,我不知道哪里错了,代码在运行时给出了错误的结果: 当 i = 0 时,除法将为 1/x。所以我在启动时分配 temp = 1/x 。对于每次迭代,我在 "i" 之后更改 "temp"。 我使用连续循环,直到两个下一个值非常 "near" 在一起。当下两个数的差值很小的时候,我会return这个值。
我的测试用例: 输入是 x =1,所以 excected arcsin(X) 将是 arcsin (1) = PI/2 = 1.57079633 rad.
class Arc{
static double abs(double x)
{
return x >= 0 ? x : -x;
}
static double pow(double mu, long n)
{
double kq = mu;
for(long i = 2; i<= n; i++)
{
kq *= mu;
}
return kq;
}
static long fact(long n)
{
long gt = 1;
for (long i = 2; i <= n; i++) {
gt *= i;
}
return gt;
}
#region arcsin
static double arcsinX(double x) {
int i = 0;
double temp = 0;
while (true)
{
//i++;
var iFactSquare = fact(i) * fact(i);
var tempNew = (double)fact(2 * i) / (pow(4, i) * iFactSquare * (2*i+1)) * pow(x, 2 * i + 1) ;
if (abs(tempNew - temp) < 0.00000001)
{
return tempNew;
}
temp = tempNew;
i++;
}
}
public static void Main(){
Console.WriteLine(arcsin());
Console.ReadLine();
}
}
注意: 我将此作为社区维基答案,因为我不是第一个想到这个的人(只是第一个在评论中写下它的人)。如果您觉得需要添加更多内容才能使答案完整,请在其中进行编辑!
普遍怀疑这是因为 Integer Overflow,即您的其中一个值(可能是 fact()
或 iFactSquare()
的 return)变得太大了对于您选择的类型。它会变成负数,因为你使用的是有符号类型——当它变成一个太大的正数时,它会循环回到负数。
尝试跟踪计算过程中 n
的大小,并计算出如果通过 fact
、[=14] 运行 这个数字,它会给你多大的数字=] 和 iFactSquare
函数。如果它大于 Maximum long value in 64-bit like we think (assuming you're using 64-bit, it'll be a lot smaller for 32-bit), then try using a double instead.
在很多series evaluation中,经常使用term之间的商来更新term会很方便。这里的商是
(2n)!*x^(2n+1) 4^(n-1)*((n-1)!)^2*(2n-1)
a[n]/a[n-1] = ------------------- * --------------------- -------
(4^n*(n!)^2*(2n+1)) (2n-2)!*x^(2n-1)
=(2n(2n-1)²x²)/(4n²(2n+1))
= ((2n-1)²x²)/(2n(2n+1))
因此计算系列值的循环是
sum = 1;
term = 1;
n=1;
while(1 != 1+term) {
term *= (n-0.5)*(n-0.5)*x*x/(n*(n+0.5));
sum += term;
n += 1;
}
return x*sum;
仅保证 abs(x)<1
的收敛,对于 x=1
处的评估,您必须使用角度减半,这通常是加速收敛的好主意。
您正在保存两个不同的临时值(temp 和 tempNew)以检查继续计算是否无关紧要。这很好,只是您没有保存这两个值的总和。
这是总结。您需要将每个新的计算值添加到总数中。您只跟踪最近计算的值。您只能 return 系列的最后一个计算值。所以你的结果总是会得到一个非常小的数字。将其转化为求和,问题就会消失。