2009-09-26 15 views
5

Chciałbym przybliżyć wartość e do jakiejkolwiek pożądanej precyzji. Jaki jest najlepszy sposób na zrobienie tego? Najbardziej udało mi się uzyskać e = 2,7182818284590455. Dowolne przykłady modyfikacji następującego kodu będą mile widziane.Lepsze przybliżenie ei Javy

public static long fact(int x){ 
    long prod = 1; 
    for(int i = 1; i <= x; i++) 
     prod = prod * i; 
    return prod; 
}//fact 

public static void main(String[] args) { 
    double e = 1; 
    for(int i = 1; i < 50; i++) 
     e = e + 1/(double)(fact(i)); 
    System.out.print("e = " + e); 
}//main 
+5

Oczywiście podwójny nigdy nie może zawierać więcej cyfr niż jego dokładność. Użyj innego rodzaju numeru. – Joren

+2

Bardzo, co powiedział Joren. Idea "dowolnej pożądanej precyzji" jest zasadniczo niekompatybilna z obliczeniami w typie o stałej szerokości. –

Odpowiedz

13

Użyj numeru BigDecimal zamiast podwójnego.

BigDecimal e = BigDecimal.ONE; 
BigDecimal fact = BigDecimal.ONE; 

for(int i=1;i<100;i++) { 
    fact = fact.multiply(new BigDecimal(i)); 

    e = e.add(BigDecimal.ONE.divide(fact, new MathContext(10000, RoundingMode.HALF_UP))); 
} 
+0

Implementacja tego podanego w czasie wykonywania: Wyjątek w wątku "główny" java.lang.ArithmeticException: Niekończąca się ekspansja dziesiętna; brak dokładnego reprezentatywnego wyniku dziesiętnego. Czy czegoś brakuje? – Sharon

+0

Przepraszamy. Division potrzebuje RoundingMode, inaczej BigDecimal rzuca się, gdy widzi 1/3. Naprawione. – Zed

+0

Jak tego użyć? Wykonuję System.out.println (e) po pętli i uzyskuję 3 – flybywire

3

Będziesz mieć lepsze wyniki, jeśli policzysz od 49 do 1 zamiast od 1 do 49 jak teraz.

6

Twoim głównym problemem jest to, że double ma bardzo ograniczoną precyzję. Jeśli chcesz uzyskać dowolną precyzję, musisz użyć BigDecimal. Kolejny problem, który napotkasz, to ograniczony zakres long, który bardzo szybko przekroczysz z silnią - tam możesz użyć BigInteger.

4

Czy spojrzałeś na arytmetykę arbitralnej precyzji w java.util.BigDecimal?

import java.math.BigDecimal; 
import java.math.MathContext; 
public class BigExp { 
    public static void main(String[] args) { 
BigDecimal FIFTY =new BigDecimal("50"); 
BigDecimal e = BigDecimal.ZERO; 
BigDecimal f = BigDecimal.ONE; 
MathContext context = new MathContext(1000); 

for (BigDecimal i=BigDecimal.ONE; i.compareTo(FIFTY)<0; i=i.add(BigDecimal.ONE)) { 
    f = f.multiply(i, context); 
    e = e.add(i.divide(f,context),context); 

    System.out.println("e = " + e); 
} 
    } 
} 
+0

to działa dla mnie, ale odpowiedź Zeda (przyjęta) nie oznacza, że: – flybywire

1

Poszedłem z odmianą Zeda i mobrule's code. Działa świetnie, dzięki! Ktoś lepiej radzi sobie z wydajnością?

public static BigDecimal factorial(int x){ 
    BigDecimal prod = new BigDecimal("1"); 
    for(int i = x; i > 1; i--) 
     prod = prod.multiply(new BigDecimal(i)); 
    return prod; 
}//fact 

public static void main(String[] args) { 
    MathContext mc = new MathContext(1000); 
    BigDecimal e = new BigDecimal("1", mc); 
    for(int i = 1; i < 1000; i++) 
     e = e.add(BigDecimal.ONE.divide(factorial(i), mc)); 
    System.out.print("e = " + e); 
}//main 
+0

Zaleca wykonanie: przestań przeliczać silnię. Użyj rozwiązania Zeda, gdy wykonujesz "fact = fact.multiply (new BigDecimal (i))" w pętli, zamiast obliczać ją ponownie. –

+0

Jako dowód, rozwiązanie mobrule działa w 542ms, twoja wersja działa w 858ms na 1000 iteracji. –

1

Większa wydajność rada ktoś?

Tak, twoje obliczenie silni jest tak samo nieefektywne, jak to tylko możliwe. Lepiej przenieść to w pętli, w której podsumowujesz warunki. Sposób, w jaki to robisz, zmienia problem O (N) w problem O (N^2).

A jeśli to było prawdziwe obliczenie, które wymagało silni, poleciłbym sprawdzanie tabeli lub niekompletną funkcję gamma jako właściwy sposób.

Jedyną rzeczą, która mogłaby być gorsza z punktu widzenia wydajności, jest rekurencyjne obliczenie czynnikowe. Wtedy masz dodatkowy problem z ogromnym stosem.