Python-计算arcsin时不准确



我正在尝试在Python中实现arcsin,而不使用任何外部库。

这是我的代码:

from time import process_time as pt
class TrigoCalc(metaclass=__readonly):
# This class evaluates various Trigonometric functions 
# including Inverse Trigonometric functions
def __setattr__(self, name, value):
raise Exception("Value can't be changed")

@staticmethod
def asin(x):
'''Implementation from Taylor series
asin(x) => summation[(2k)! * x^(2k + 1) / (2^(2k) * (k!)^2 * (2k + 1))]
k = [0, inf)
x should be real
'''
# a0 = 1                                                                           
# a1 = 1/(2*3)                                                                     
# a2 = 1/2 * 3/(4*5) 
# a3 = 1/2 * 3/4 * 5/(6*7)
# a4 = 1/2 * 3/4 * 5/6 * 7/(8*9)
# a5 = 1/2 * 3/4 * 5/6 * 7/8 * 9/(10*11)
# a6 = 1/2 * 3/4 * 5/6 * 7/8 * 9/10 * 11/(12*13)
# a7 = 1/2 * 3/4 * 5/6 * 7/8 * 9/10 * 11/12 * 13/(14*15)
# a8 = 1/2 * 3/4 * 5/6 * 7/8 * 9/10 * 11/12 * 13/14 * 15/(16*17)
# a9 = 1/2 * 3/4 * 5/6 * 7/8 * 9/10 * 11/12 * 13/14 * 15/16 * 17/(18*19)
# a10 = 1/2 * 3/4 * 5/6 * 7/8 * 9/10 * 11/12 * 13/14 * 15/16 * 17/18 * 19/(20*21)

# taking 10 coefficients for arriving at a common sequence

# N = n, D = n + 1; (N/D) --> Multiplication, number of times the coefficient number, n >= 1

start_time = pt()
coeff_list = []
NUM_ITER = 10000
for k in range(NUM_ITER):
if k == 0:
coeff_list.append(1)
else:
N = 1
D = N + 1
C = N/D
if k >= 2:
for i in range(k-1):
N += 2; D += 2
C = C * N/D
coeff_list.append(C)

__sum = 0
for k in range(NUM_ITER):
n = coeff_list[k] * math_utils.power(x, 2*k + 1) / (2*k + 1)
__sum += n

# Radian conversion to degrees
__sum = __sum/TrigoCalc.pi * 180

end_time = pt()
print(f'Execution time: {end_time - start_time} seconds')
return __sum

结果

NUM_ITER60(无穷级数迭代60次(时,在x = 1极点的计算存在显著的不精确性,而x = 1/2给出了14点的精度。

In [2]: TrigoCalc.asin(0.5)
Execution time: 0.0 seconds
Out[2]: 30.000000000000007
In [3]: TrigoCalc.asin(1)
Execution time: 0.0 seconds
Out[3]: 85.823908877692

两次运行的执行时间都不明显。

NUM_ITER10000时,在x = 1极点,结果比以前的运行更准确,但在x = 1/2,精度完全相同。

In [4]: TrigoCalc.asin(0.5)
Execution time: 19.109375 seconds
Out[4]: 30.000000000000007
In [5]: TrigoCalc.asin(1)
Execution time: 19.109375 seconds
Out[5]: 89.67674183336727 

对于这种类型的计算,这两次运行的执行时间非常高。

问题

如何平衡代码,使其在较小的NUM_ITER中在x = 1极点处至少具有1点精度?

请随时为代码提供建议或更新。

Python版本:3.7.7

编辑:借助@Joni的答案更改代码以获得精确结果

  1. 将无穷级数计算封装到asin():内的另一个函数中

    def asin(x):     
    def __arcsin_calc(x):                                        
    # ....                                                 
    # Computations                                               
    # ....                                                             
    # Removing the radian to degree conversion from this function             
    return __sum            
    
  2. 使用asin()内部的新函数为x添加限制以避免缓慢收敛:

    if -1.0 <= x < -0.5:
    return -(TrigoCalc.pi/2 - __arcsin_calc(math_utils.power((1 - x*x), 0.5))) / TrigoCalc.pi * 180    # Radian to Degree conversion
    elif -0.5 <= x <= 0.5:
    return __arcsin_calc(x)/TrigoCalc.pi * 180
    elif 0.5 < x <= 1.0:          
    return (TrigoCalc.pi/2 - __arcsin_calc(math_utils.power((1 - x*x), 0.5))) / TrigoCalc.pi * 180
    else:      
    raise ValueError("x should be in range of [-1, 1]")
    
  3. 结果:

    In [2]: TrigoCalc.asin(0.99)
    Execution time: 0.0 seconds
    Out[2]: 81.89022502527023
    In [3]: math.asin(0.99)/TrigoCalc.pi*180
    Out[3]: 81.89038554400582
    In [4]: TrigoCalc.asin(1)
    Execution time: 0.0 seconds
    Out[4]: 90.0
    In [5]: math.asin(1)/TrigoCalc.pi*180
    Out[5]: 90.0
    

arcsin(1)的问题是arcsin(x)在x=1时是垂直的(导数在没有约束的情况下增长(。像泰勒级数这样的多项式逼近跟不上。你会得到非常慢的收敛,需要大量的项才能得到一个合适的逼近。你需要改变处理问题的方式。

例如,对于小x,y = sin(pi/2 - x)近似为1 - x^2/2,从中可以导出近似值asin(y) = pi/2 - sqrt(2 - 2*y)。这种近似值适用于非常接近1的值-您可以直接使用它。

如果你再努力一点,你就可以证明准确的身份

asin(x) = pi/2 - 2*asin( sqrt( (1-x)/2 ) )

使用此标识,您可以使用现有的asin函数为1附近的x计算asin(x),该函数适用于0附近的x。

例如:要计算asin(0.99),您需要计算:

asin(0.99) = pi/2 - 2*asin( sqrt( (1-.99)/2 ) )
= pi/2 - 2*asin( sqrt(.005) )
= pi/2 - 2*asin(0.07071067811865475)

然后使用现有算法来获得CCD_ 22的高质量近似值。

这是用于生产质量数学库实现的技术——例如,请参阅OpenLibm或fdlibm。

一个极其基本的近似值将给出sum from 0 to N1e(-N)处近似arcsin(以弧度表示(。这里,您给出的是以度为单位的结果,因为度和弧度之间大约有一个1e2的比率,所以您需要将NUM_ITER = 1e(N+2)设置为1e(-N)处的近似arcsin。

因此,对于您的特定问题,您需要使用N = 1(大约1分(进行测试,因此NUM_ITER = 1e(1+2) = 1,000。这一点都不准确,但可以让你了解你正在寻找的价值。

然后,如果你想寻找确切的值,我看不出每次都可以使用精确的数学方法(对于任何x.point精度(。但是,如果NUM_ITER是您的算法的目标,您可以使用二分法算法来查找它。第一个近似值将减少计算时间。

精确的近似值来自于x^O(n)4^O(n)的比值,4^O(n)较大。我们可以用CCD_ 35来近似求和项。

如果有人能做一个精确的微积分,我会非常高兴看到它

最新更新