有没有人有一个简单的算法来计算一个相当准确的反正弦?“简单”是指某种多项式,每个输出样本需要 <= 5 次乘法。我所说的“相当准确”是指当输入参数接近正负一时,其误差不超过 10% 的算法。我在网上搜索了一段时间,但没有发现任何立即有用的东西。
寻找反正弦算法
function y = arcsin_test3(x)
y = x.*(1+x.*x.*(1/6+ x.*x.*(3/(2*4*5) + x.*x.*((1*3*5)/(2*4*6*7)))))
endfunction
这似乎有五个乘法(假设您可以保存 的结果x.*x
)和三个加法。
scilab
情节是:
顶部是scilab
's asin
vs this ,底部是两者之间的误差。
原始答案
这里的平方根可能很麻烦,但我想我会把它写下来,因为它看起来很有趣。:-)
该页面建议:
来自 Milton Abramowitz 和 Irene Stegun 的《数学函数手册》第 81 页:
我已经实现了这个scilab
,它工作正常,除了 $x= -1$ 左右。只需将 $0 \le x \le 1$ 反映到 $-1 \le x \le 0$ 就可以获得更好的近似值。. Just reflecting the over to makes for a much better approximation.
上图显示了scilab
'asin
函数与上述近似值(红色虚线)相对于我的绿色变化。
底部图显示了我的更改的错误(将其与原图绘制在同一轴上意味着绿色在任何地方看起来都为零)。
// 25770
function y = arcsin_test(x)
a0 = 1.5707288
a1 = -0.2121144
a2 = 0.0742610
a3 = -0.0187293
xx = abs(x)
y = %pi/2 - sqrt(1-x).*(a0 + a1*x + a2.*x.*x + a3.*x.*x.*x)
endfunction
function y = arcsin_test2(x)
a0 = 1.5707288
a1 = -0.2121144
a2 = 0.0742610
a3 = -0.0187293
xx = abs(x)
y = %pi/2 - sqrt(1-xx).*(a0 + a1*xx + a2.*xx.*xx + a3.*xx.*xx.*xx)
y = y.*sign(x);
endfunction
x = [-1: .0100001 : 1];
clf
subplot(211)
plot(x,arcsin_test2(x),'g.');
plot(x,arcsin_test(x),'r:');
plot(x,asin(x))
subplot(212)
//plot(x,(arcsin_test(x) - asin(x)),'r:')
plot(x,(arcsin_test2(x) - asin(x)),'g.')
曲线的中心部分不是真正的问题,因为它是相当线性的,并且泰勒近似两个或三个项是一个很好的起点(最小二乘多项式拟合稍好)。
由于无限坡度,侧面更成问题。一种应对方法是通过变换
这涉及平方根。
如果您的参数 $z$ 用浮点数表示,则通过将指数减半并对尾数应用线性变换来获得平方根的快速近似值。 is represented with floating-point, a fast approximation of the square root is obtained by halving the exponent and applying a linear transform to the mantissa.
令$z=m2^e$,其中$1\le m<2$,则$\sqrt z=\sqrt m\,2^{e/2}$。你可以用 $(\sqrt2-1)(m+\sqrt2)$ 来近似 $\sqrt{m}$。, with , then . You can approximate by .
- 将指数 $e$ 分开(清除它产生 $m$ 的表示); apart (clearing it yields the representation of );
- 如果$e$ 是偶数,则计算$(\sqrt2-1)(m+\sqrt2)$; is even, compute ;
- 如果$e$ 是奇数,则计算$\sqrt2(\sqrt2-1)(m+\sqrt2)$; is odd, compute ;
- 设置指数 $\lfloor e/2\rfloor$。.
你可以使用 Infinite Series 来计算 arctan,然后你可以使用 arctan & sqrt 来计算 arcsin。
注意:未拦截的边缘情况 - 可能会在不连续点(即 divZero)处爆炸。
public MyRational Arctan()
{
// https://en.wikipedia.org/wiki/Inverse_trigonometric_functions#Infinite_series
MyRational sum = new MyRational(0);
MyRational epsilon = new MyRational(1, 1000000000000);
for (int n = 0; true; ++n)
{
MyRational dividend = MyRational.MinusOne.Pow(n) * this.Pow(2 * n + 1);
MyRational divisor = new MyRational(2 * n + 1);
MyRational quotient = dividend.Divide(divisor);
MyRational newSum = sum + quotient;
if (newSum.Subtract(sum).Abs() < epsilon)
{
return newSum;
}
sum = newSum;
} // Next i
}
public MyRational Arcsin()
{
// https://en.wikipedia.org/wiki/Inverse_trigonometric_functions#Extension_to_complex_plane
// https://dsp.stackexchange.com/questions/25770/looking-for-an-arcsin-algorithm
MyRational divisor = Sqrt(One.Subtract(this.Pow(2)), new MyRational(1, 10000));
MyRational x = this.Divide(divisor);
return x.Arctan();
}
// https://www.geeksforgeeks.org/find-root-of-a-number-using-newtons-method/
public static MyRational Sqrt(MyRational radicand, MyRational epsilon)
{
// Assuming the sqrt of radicand as radicand only
MyRational x = radicand;
// The closed guess will be stored in the root
MyRational root;
// To count the number of iterations
int count = 0;
while (true)
{
count++;
// Calculate more closed x
root = MyRational.OneHalf * (x + (radicand / x));
// Check for closeness
if ((root - x).Abs() < epsilon)
break;
// Update root
x = root;
} // Whend
return root;
} // End Function SquareRoot