2012-12-05 12 views
7

Mam trochę kodu, który używa zmodyfikowanych funkcji Bessela zarówno pierwszego, jak i drugiego rzędu (iv i kv). Irytujące wydają się mieć ograniczenia, są to iv (0,713) i kv (0,697), dodają po jednym, a ty dostajesz odpowiednio nieskończoność i 0. Jest to dla mnie problem, ponieważ muszę użyć wartości wyższych niż ta, często do 2000 lub więcej. Kiedy próbuję przez nie podzielić, kończę nurkowanie 0 lub nieskończoność, co oznacza, że ​​albo otrzymuję błędy, albo zero, z których żadne nie chcę.Funkcje Bessela w Pythonie, które działają z dużymi wykładnikami

Używam scipy bessel functions, czy są jakieś lepsze funkcje, które radzą sobie z dużo mniejszymi i znacznie większymi liczbami lub sposób modyfikowania Pythona do pracy z tymi dużymi liczbami. Nie jestem pewien, jaki jest prawdziwy problem, dlaczego Python nie może ich przetworzyć poza 700, czy jest to funkcja, czy to Python?

Nie wiem, czy Python już to robi, ale potrzebuję tylko pierwszych 5-10 cyfr * 10^x na przykład; to znaczy, że nie potrzebowałbym wszystkich 1000 cyfr, być może jest to problem z tym, jak działa Python w porównaniu do tego, jak pracuje Wolfram Alpha?

+2

Nie sądzę Jego problem Pythona tyle jako podwójne pływającym punktem kwestii zakresu. scipy dostarcza wrapper wokół kodu C, który faktycznie implementuje funkcję bessel. Jako taki jest ograniczony do zakresu, który może pomieścić podwójny. – sizzzzlerz

+0

tak, właśnie zrobiłem sys.float_info i zgadnij co? max_10_exp = 308, która jest mniej więcej dokładną odpowiedzią funkcji bessela na granicy. To dla mnie zła wiadomość. W jaki sposób Wolfram Alpha jest w stanie to rozwiązać? – Rapid

+1

Magia? Nie wiem, ale jestem prawie pewien, że Alpha dostaje swoją bazę kodu z Mathematica Wolframa, który jest dość wyrafinowanym narzędziem. Wdrożyli jakiś algorytm, który pozwala im zwrócić zasadniczo nieograniczoną precyzję i zakres dla funkcji transcendentalnych, takich jak bessel. – sizzzzlerz

Odpowiedz

7

Funkcje w Scipy są mniej więcej tak dobre, jak można uzyskać przy użyciu zmiennoprzecinkowej maszyny podwójnej precyzji. Jak zauważono w powyższych komentarzach, pracujesz w zakresie, w którym wynik przekracza zakres zmiennoprzecinkowy.

Można użyć biblioteki mpmath, która wykonuje zmiennoprzecinkową precyzję (oprogramowanie), aby obejść ten problem. (Jest to podobne do MPFR, ale w Pythonie):

In [1]: import mpmath 

In [2]: mpmath.besseli(0, 1714) 
mpf('2.3156788070459683e+742') 

In [3]: mpmath.besselk(0, 1714) 
mpf('1.2597398974570405e-746') 
+0

Wydaje się, że otrzymujesz prawidłowe odpowiedzi, więc dziękuję bardzo. Jedyny problem, jaki mam, to włączenie elementów mpf do reszty mojego kodu. Ponieważ nie jest to float, daje mi mnóstwo błędów, a mpf nie akceptuje tablic? Odpowiedziałeś na moje pytanie, więc będę musiał przeprowadzić sporo badań w mpf i mpfr itd. Czy nie ma sposobu, aby 1.2597e-746 stroed jako float, mam na myśli to tylko 11 rzeczy do przechowywania. Hmmm. – Rapid

+1

Musisz zachować wszystko jako 'mpf'. Dobrym pomysłem może być jednak praca z logarytmami małych liczb, a nie z samych logarytmami. Jeśli potrzebujesz dodać liczby, użyj 'logaddexp'. Możesz też ponownie przemyśleć problem i zrobić trochę matematyki, aby spróbować pozbyć się ogromnych i małych liczb. –

+0

@Rapid: Możesz napisać własną klasę, aby przechowywać punkty dziesiętne i wykładnik oddzielnie, jeśli chcesz, ale czy to naprawdę warte wysiłku? – endolith

3

mpmath jest fantastyczny biblioteki i jest droga dla obliczeń o wysokiej precyzji. Warto zauważyć, że funkcje te można obliczyć na podstawie ich bardziej podstawowych składników. W związku z tym nie jesteś zmuszony przestrzegać ograniczeń scipy i możesz użyć innej biblioteki o wysokiej precyzji. Minimalny przykład poniżej:

import numpy as np 
from scipy.special import * 

X = np.random.random(3) 

v = 2.000000000 

print "Bessel Function J" 
print jn(v,X) 

print "Modified Bessel Function, Iv" 
print ((1j**(-v))*jv(v,1j*X)).real 
print iv(v,X) 

print "Modified Bessel Function of the second kind, Kv" 
print (iv(-v,X)-iv(v,X)) * (np.pi/(2*sin(v*pi))) 
print kv(v,X) 

print "Modified spherical Bessel Function, in" 
print np.sqrt(np.pi/(2*X))*iv(v+0.5,X) 
print [sph_in(floor(v),x)[0][-1] for x in X] 

print "Modified spherical Bessel Function, kn" 
print np.sqrt(np.pi/(2*X))*kv(v+0.5,X) 
print [sph_kn(floor(v),x)[0][-1] for x in X] 

print "Modified spherical Bessel Function, in" 
print np.sqrt(np.pi/(2*X))*iv(v+0.5,X) 
print [sph_in(floor(v),x)[0][-1] for x in X] 

Daje:

Bessel Function J 
[ 0.01887098 0.00184202 0.08399226] 

Modified Bessel Function, Iv 
[ 0.01935808 0.00184656 0.09459852] 
[ 0.01935808 0.00184656 0.09459852] 

Modified Bessel Function of the second kind, Kv 
[ 12.61494864 135.05883902 2.40495388] 
[ 12.61494865 135.05883903 2.40495388] 

Modified spherical Bessel Function, in 
[ 0.0103056 0.00098466 0.05003335] 
[0.010305631072943869, 0.00098466280846548084, 0.050033450286650107] 

Modified spherical Bessel Function, kn 
[ 76.86738631 2622.98228411  6.99803515] 
[76.867205587011171, 2622.9730878542782, 6.998023749439338] 

Modified spherical Bessel Function, in 
[ 0.0103056 0.00098466 0.05003335] 
[0.010305631072943869, 0.00098466280846548084, 0.050033450286650107] 

to zawiedzie dla dużych wartościach szukasz, chyba że dane źródłowe ma wysoką precyzję.

+0

Jeśli te błędy również zawiedą w przypadku dużych wartości, to w jaki sposób jest to odpowiedź? Czy są bardziej precyzyjne niż dedykowane funkcje? – endolith

+0

@endolith Przepraszamy, jeśli ta ostatnia linia nie jest jasna. Imponuję, że duże wartości wymagały odpowiednio dużej precyzji (ustawionej w 'mpmath'), aby była dokładna. Zaletą korzystania z mpmath jest możliwość ustawienia tej wartości. – Hooked

1

Może to być problem związany z funkcją. Dla dużych dodatnich x istnieje asymptotyczny kv (nu, x) ~ e^{- x}/\ sqrt {x} dla dowolnego nu. Tak więc dla dużych x otrzymujesz bardzo małe wartości. Jeśli jesteś w stanie pracować z logiem funkcji Bessela, problemy znikną. Scilab wykorzystuje ten asymptotyczny: ma parametr ice, który domyślnie przyjmuje wartość 0, ale po ustawieniu na 1 zwróci exp (x) * kv (nu, x), a to zachowuje wszystko w rozsądnym rozmiarze.

Właściwie, to samo jest dostępny w scipy - scipy.special.kve

Powiązane problemy