1

mpmath の "mpf" 浮動小数点 bignum の形式にある小さな値のアークサイン関数を計算する必要があります。

私が「小さい」値と呼んでいるのは、たとえば e/4/(10**7) = 0.000000067957045711476130884... です。

これは、mpmath の組み込み asin 関数を使用して私のマシンでテストした結果です。

import gmpy2
from mpmath import *
from time import time

mp.dps = 10**6

val=e/4/(10**7)
print "ready"

start=time()
temp=asin(val)
print "mpmath asin: "+str(time()-start)+" seconds"

>>> 155.108999968 seconds

これは特定のケースです。私はやや小さい数を扱っているので、この特定のケース (= 小さい値) で mpmath を実際に上回る Python で計算する方法があるかどうかを自問しています。

テイラー級数は、小さな引数に対して非常に高速に収束するため、ここでは実際に良い選択です。しかし、どうにかして計算をさらに高速化する必要があります。

実際にはいくつかの問題があります:
1) 引数を小さな分数として記述できる場合にのみ有効になるため、2 分割はここでは効果がありません。ここでは完全精度の float が与えられます。
2) arcsin は非交互級数であるため、Van Wijngaarden または sumalt 変換も効果がありません (それらを非交互級数に一般化する方法を私が認識していない場合を除きます)。 https://en.wikipedia.org/wiki/Van_Wijngaarden_transformation

私が考えることができる唯一の加速度は、チェビシェフ多項式です。逆正弦関数にチェビシェフ多項式を適用できますか? 方法?

4

3 に答える 3

2

mpfrgmpy2に同梱されている型は使えますか?

>>> import gmpy2
>>> gmpy2.get_context().precision = 3100000
>>> val = gmpy2.exp(1)/4/10**7
>>> from time import time
>>> start=time();r=gmpy2.asin(val);print time()-start
3.36188197136

GMP ライブラリのサポートに加えて、gmpy2 は MPFR および MPC 多倍精度ライブラリもサポートしています。

免責事項: 私は gmpy2 を保守しています。

于 2013-08-14T18:11:17.553 に答える
1

実際、バイナリ分割は、分子と分母のサイズに対して項の数をバランスさせるために反復引数削減と組み合わせると、非常にうまく機能します (これはビットバースト アルゴリズムとして知られています)。

以下は、式 atan(t) = atan(p/2^q) + atan((t*2^qp) / (2^q+p*t)) の繰り返し適用に基づく mpmath のバイナリ分割の実装です。この式は、Richard Brent によって最近提案されました (実際、mpmath の atan は、キャッシュから atan(p/2^q) を検索するために、この式の単一の呼び出しを低精度で既に使用しています)。私の記憶が正しければ、MPFR もビットバースト アルゴリズムを使用して atan を評価しますが、少し異なる式を使用します。これはおそらくより効率的です (いくつかの異なる逆正接値を評価する代わりに、逆正接微分方程式を使用して分析継続を行います)。

from mpmath.libmp import MPZ, bitcount
from mpmath import mp

def bsplit(p, q, a, b):
    if b - a == 1:
        if a == 0:
            P = p
            Q = q
        else:
            P = p * p
            Q = q * 2
        B = MPZ(1 + 2 * a)
        if a % 2 == 1:
            B = -B
        T = P
        return P, Q, B, T
    else:
        m = a + (b - a) // 2
        P1, Q1, B1, T1 = bsplit(p, q, a, m)
        P2, Q2, B2, T2 = bsplit(p, q, m, b)
        T = ((T1 * B2) << Q2) + T2 * B1 * P1
        P = P1 * P2
        B = B1 * B2
        Q = Q1 + Q2
        return P, Q, B, T

def atan_bsplit(p, q, prec):
    """computes atan(p/2^q) as a fixed-point number"""
    if p == 0:
        return MPZ(0)
    # FIXME
    nterms = (-prec / (bitcount(p) - q) - 1) * 0.5
    nterms = int(nterms) + 1
    if nterms < 1:
        return MPZ(0)
    P, Q, B, T = bsplit(p, q, 0, nterms)
    if prec >= Q:
        return (T << (prec - Q)) // B
    else:
        return T // (B << (Q - prec))

def atan_fixed(x, prec):
    t = MPZ(x)
    s = MPZ(0)
    q = 1
    while t:
        q = min(q, prec)
        p = t >> (prec - q)
        if p:
            s += atan_bsplit(p, q, prec)
            u = (t << q) - (p << prec)
            v = (MPZ(1) << (q + prec)) + p * t
            t = (u << prec) // v
        q *= 2
    return s

def atan1(x):
    prec = mp.prec
    man = x.to_fixed(prec)
    return mp.mpf((atan_fixed(man, prec), -prec))

def asin1(x):
    x = mpf(x)
    return atan1(x/sqrt(1-x**2))

このコードを使用すると、次のようになります。

>>> from mpmath import *
>>> mp.dps = 1000000
>>> val=e/4/(10**7)
>>> from time import time
>>> start = time(); y1 = asin(x); print time() - start
58.8485069275
>>> start = time(); y2 = asin1(x); print time() - start
8.26498985291
>>> nprint(y2 - y1)
-2.31674e-1000000

警告: atan1 は 0 <= x < 1/2 を想定しており、項の数の決定は最適または正しくない可能性があります (これらの問題の修正は読者の課題として残されています)。

于 2013-08-16T15:12:22.023 に答える
0

手っ取り早い方法は、事前に計算されたルックアップ テーブルを使用することです。

しかし、たとえばasinのテイラー級数を見ると、

def asin(x):
    rv = (x + 1/3.0*x**3 + 7/30.0*x**5 + 64/315.0*x**7 + 4477/22680.0*x**9 + 
         28447/138600.0*x**11 + 23029/102960.0*x**13 + 
         17905882/70945875.0*x**15 + 1158176431/3958416000.0*x**17 + 
         9149187845813/26398676304000.0*x**19)
    return rv

x の値が小さい場合、asin(x) ≈ x であることがわかります。

In [19]: asin(1e-7)
Out[19]: 1.0000000000000033e-07

In [20]: asin(1e-9)
Out[20]: 1e-09

In [21]: asin(1e-11)
Out[21]: 1e-11

In [22]: asin(1e-12)
Out[22]: 1e-12

たとえば、使用した値の場合:

In [23]: asin(0.000000067957045711476130884)
Out[23]: 6.795704571147624e-08

In [24]: asin(0.000000067957045711476130884)/0.000000067957045711476130884
Out[24]: 1.0000000000000016

もちろん、この違いがあなたに関係があるかどうかによって異なります。

于 2013-08-14T17:03:15.907 に答える