寻找反正弦算法

信息处理 算法 数学
2022-01-02 12:01:42

有没有人有一个简单的算法来计算一个相当准确的反正弦?“简单”是指某种多项式,每个输出样本需要 <= 5 次乘法。我所说的“相当准确”是指当输入参数接近正负一时,其误差不超过 10% 的算法。我在网上搜索了一段时间,但没有发现任何立即有用的东西。

4个回答

这只是一个多项式版本

arcsin(x)=x+12x33+1324x55+135246x77

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 asinvs this ,底部是两者之间的误差。


原始答案

这里的平方根可能很麻烦,但我想我会把它写下来,因为它看起来很有趣。:-)

该页面建议:

来自 Milton Abramowitz 和 Irene Stegun 的《数学函数手册》第 81 页:

arcsin(x)=π/21x(a0+a1x+a2x2+a3x3),
where
a0=1.5707288a1=0.2121144a2=0.0742610a3=0.0187293

我已经实现了这个scilab,它工作正常,除了 $x= -1$ 左右。只需将 $0 \le x \le 1$ 反映到 $-1 \le x \le 0$ 就可以获得更好的近似值。x=1. Just reflecting the 0x1 over to 1x0 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.')

我在这里有一个很好的 $\arctan()$ 实现arctan()

我认为您可以使用身份:

arcsin(x)=arctan(x1x2)

得到你想要的。

曲线的中心部分不是真正的问题,因为它是相当线性的,并且泰勒近似两个或三个项是一个很好的起点(最小二乘多项式拟合稍好)。

由于无限坡度,侧面更成问题。一种应对方法是通过变换

arcsin(x)=π2arcsin(1x2),

这涉及平方根。


如果您的参数 $z$ 用浮点数表示,则通过将指数减半并对尾数应用线性变换来获得平方根的快速近似值。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}$。z=m2e, with 1m<2, then z=m2e/2. You can approximate m by (21)(m+2).

  • 将指数 $e$ 分开(清除它产生 $m$ 的表示);e apart (clearing it yields the representation of m);
  • 如果$e$ 是偶数,则计算$(\sqrt2-1)(m+\sqrt2)$;e is even, compute (21)(m+2);
  • 如果$e$ 是奇数,则计算$\sqrt2(\sqrt2-1)(m+\sqrt2)$;e is odd, compute 2(21)(m+2);
  • 设置指数 $\lfloor e/2\rfloor$。e/2.

在此处输入图像描述

你可以使用 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