Cálculo de fatoriais de números muito grandes!

11 respostas
T

Olá a todos!

Criei uma aplicação que calcula o fatorial de um inteiro x a partir do método:

public class Fatorial 
{
    public int fatorial(int x)
    {
        if (x == 0)
            return 1;
        else
            return x * fatorial(x - 1);
    }
    
}

A aplicação também avalia o valor da expressão e = 1 + 1/1! + 1/2! + 1/3! ... (n termos), onde n é dado pelo usuário. Abaixo apresentamos o código:

import java.util.Scanner;

/**
 *
 * @author Tales R. T. Carneiro
 */
public class FatorialTeste 
{

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) 
    {
        // TODO code application logic here
        
        /**
         * Local variables
         */
        int x;
        int result;
        int n;
        int count;
        double e;
        
        /**
         * Asks for an integer
         * and reads it
         */
        Scanner input = new Scanner(System.in);
        
        System.out.print("Entre com um inteiro positivo: ");
        x = input.nextInt();
        
        /**
         * Calculates the fatorial of an integer
         * and prints the result on the screen
         */
        Fatorial f = new Fatorial();
        result = f.fatorial(x);
        System.out.printf("O fatorial de %d é: %d", x, result);
        
        /**
         * Calculates the value of the expression:
         * e = 1 + 1 / 1! + 1 / 2! + 1 / 3! ... (n terms)
         */
        System.out.print("Entre com um inteiro n > 0: ");
        n = input.nextInt();
        
        if (n == 1)
            e = 1;
        else
        {
            count = 1;
            e = 1;
            while (count < n)
            {
                e = e + 1 / (double) f.fatorial(count);
                count++;
            }
        }
        
        System.out.printf("e = %.8f", e);
    }
}

Acontece que quando calculo um fatorial de um inteiro muito grande o valor avaliado pelo java é zero. Ao mesmo tempo quando n muito grande temos e = infinito quando na verdade a série deveria convergir. Ou seja, quando utilizamos números muito elevados o programa produz resultados errados. Por que isso acontece?

11 Respostas

B
talesc:
Olá a todos!

Criei uma aplicação que calcula o fatorial de um inteiro x a partir do método:

public class Fatorial 
{
    public int fatorial(int x)
    {
        if (x == 0)
            return 1;
        else
            return x * fatorial(x - 1);
    }
    
}

A aplicação também avalia o valor da expressão e = 1 + 1/1! + 1/2! + 1/3! ... (n termos), onde n é dado pelo usuário. Abaixo apresentamos o código:

import java.util.Scanner;

/**
 *
 * @author Tales R. T. Carneiro
 */
public class FatorialTeste 
{

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) 
    {
        // TODO code application logic here
        
        /**
         * Local variables
         */
        int x;
        int result;
        int n;
        int count;
        double e;
        
        /**
         * Asks for an integer
         * and reads it
         */
        Scanner input = new Scanner(System.in);
        
        System.out.print("Entre com um inteiro positivo: ");
        x = input.nextInt();
        
        /**
         * Calculates the fatorial of an integer
         * and prints the result on the screen
         */
        Fatorial f = new Fatorial();
        result = f.fatorial(x);
        System.out.printf("O fatorial de %d é: %d", x, result);
        
        /**
         * Calculates the value of the expression:
         * e = 1 + 1 / 1! + 1 / 2! + 1 / 3! ... (n terms)
         */
        System.out.print("Entre com um inteiro n > 0: ");
        n = input.nextInt();
        
        if (n == 1)
            e = 1;
        else
        {
            count = 1;
            e = 1;
            while (count < n)
            {
                e = e + 1 / (double) f.fatorial(count);
                count++;
            }
        }
        
        System.out.printf("e = %.8f", e);
    }
}

Acontece que quando calculo um fatorial de um inteiro muito grande o valor avaliado pelo java é zero. Ao mesmo tempo quando n muito grande temos e = infinito quando na verdade a série deveria convergir. Ou seja, quando utilizamos números muito elevados o programa produz resultados errados. Por que isso acontece?

talesc, eu acho que este tópico aqui te ajuda, valeu!

http://www.guj.com.br/java/226719-resolvido-fatoriais-de-1-a-40

T

Ajudou sim! Mas eu queria manter uma classe Fatorial em separado, em outra classe fora daquela com o método main.

Luiz_Augusto_Prado

não entendi. pq acha que não dá pra fazer isso que quer se utilizar big integer?

E

Aqui dou duas formas de calcular o número e.
Uma delas é somar os elementos do maior para o menor (ou seja, 1/0! + 1/1! + 1/2! …)
A outra, que é a mais exata, é somar os elementos do menor para o maior (ou seja, 1/24! + 1/23! + 1/22! … + 1/2! + 1/1! + 1/0!)
Tanto na primeira quanto na segunda eu uso a fórmula padrão, mas o cálculo do fatorial é implícito, não explícito. Isso economiza um bocadinho de tempo, já que não preciso ficar recalculando o fatorial para cada termo da expressão que calcula e.

class CalculoE {
    public double calcularE () {
        double soma = 0.0;
        double fator = 1.0;
        for (int n = 1; n <= 24; ++n) {
            soma += fator;
            fator /= n;
        }
        return soma;
    }

    public double calcularE_metodo2() {
        double[] fatores = new double [25];
        double soma = 0.0;
        double fator = 1.0;
        for (int n = 1; n <= 24; ++n) { 
            fatores[n] = 1.0 / fator;
            fator *= n;
        }
        for (int n = 24; n >= 1; n--) {
            soma += fatores [n];
        }
        return soma;
    }


    public static void main (String[] args) {
        CalculoE c = new CalculoE();

        // Valor esperado: 2,7182818284590452353602874713527...
        // Valor impresso: 2.7182818284590455 - note que o último dígito é 5, não 2 (diferença: +3)
        System.out.println (c.calcularE());
        // Valor impresso: 2.7182818284590450 - note que o último dígito é 0, não 2 (diferença: -2) 
        System.out.println (c.calcularE_metodo2());
    }
}
Luiz_Augusto_Prado
entanglement:
Aqui dou duas formas de calcular o número e. Uma delas é somar os elementos do maior para o menor (ou seja, 1/0! + 1/1! + 1/2! ....) A outra, que é a mais exata, é somar os elementos do menor para o maior (ou seja, 1/24! + 1/23! + 1/22! ... + 1/2! + 1/1! + 1/0!) Tanto na primeira quanto na segunda eu uso a fórmula padrão, mas o cálculo do fatorial é implícito, não explícito. Isso economiza um bocadinho de tempo, já que não preciso ficar recalculando o fatorial para cada termo da expressão que calcula e.
class CalculoE {
    public double calcularE () {
        double soma = 0.0;
        double fator = 1.0;
        for (int n = 1; n <= 24; ++n) {
            soma += fator;
            fator /= n;
        }
        return soma;
    }

    public double calcularE_metodo2() {
        double[] fatores = new double [25];
        double soma = 0.0;
        double fator = 1.0;
        for (int n = 1; n <= 24; ++n) { 
            fatores[n] = 1.0 / fator;
            fator *= n;
        }
        for (int n = 24; n >= 1; n--) {
            soma += fatores [n];
        }
        return soma;
    }


    public static void main (String[] args) {
        CalculoE c = new CalculoE();

        // Valor esperado: 2,7182818284590452353602874713527...
        // Valor impresso: 2.7182818284590455 - note que o último dígito é 5, não 2 (diferença: +3)
        System.out.println (c.calcularE());
        // Valor impresso: 2.7182818284590450 - note que o último dígito é 0, não 2 (diferença: -2) 
        System.out.println (c.calcularE_metodo2());
    }
}

muito legal!

O estudo das séries de Taylor e MacLaurin são fundamentais para desenvolvimento de programas que trabalham com calculo pesado.

vc viu se essa é a formula conhecida que converge mais rápido?

regis_hideki

Com as informações que deram, já dá pra resolver o problema, mas acho que ainda não dá pra entender muito bem porque esse erro ocorre. Se quiser entender porque números muito grandes podem resultar em valores negativos e números muito pequenos podem resultar em zero, recomendo dar uma pesquisada sobre overflow e underflow.

E

Do jeito que ele calculou o fatorial (acumulando os resultados em um int, e retornando um int) vai dar problemas mesmo.

É que quando o resultado de uma multiplicação de dois ints excede o valor de um int, o resultado é indefinido (pode ser negativo, positivo, zero etc.).

Se a pessoa que fez esse cálculo que “diverge” visse que o valor do fatorial, para n > 12, dá resultados errados do jeito que ele escreveu, veria que na verdade o cálculo é que está sendo feito indevidamente.

A série de Taylor para o cálculo de “e” é muito estável e não “diverge” de forma alguma.

B
Luiz Augusto Prado:
entanglement:
Aqui dou duas formas de calcular o número e. Uma delas é somar os elementos do maior para o menor (ou seja, 1/0! + 1/1! + 1/2! ....) A outra, que é a mais exata, é somar os elementos do menor para o maior (ou seja, 1/24! + 1/23! + 1/22! ... + 1/2! + 1/1! + 1/0!) Tanto na primeira quanto na segunda eu uso a fórmula padrão, mas o cálculo do fatorial é implícito, não explícito. Isso economiza um bocadinho de tempo, já que não preciso ficar recalculando o fatorial para cada termo da expressão que calcula e.
class CalculoE {
    public double calcularE () {
        double soma = 0.0;
        double fator = 1.0;
        for (int n = 1; n <= 24; ++n) {
            soma += fator;
            fator /= n;
        }
        return soma;
    }

    public double calcularE_metodo2() {
        double[] fatores = new double [25];
        double soma = 0.0;
        double fator = 1.0;
        for (int n = 1; n <= 24; ++n) { 
            fatores[n] = 1.0 / fator;
            fator *= n;
        }
        for (int n = 24; n >= 1; n--) {
            soma += fatores [n];
        }
        return soma;
    }


    public static void main (String[] args) {
        CalculoE c = new CalculoE();

        // Valor esperado: 2,7182818284590452353602874713527...
        // Valor impresso: 2.7182818284590455 - note que o último dígito é 5, não 2 (diferença: +3)
        System.out.println (c.calcularE());
        // Valor impresso: 2.7182818284590450 - note que o último dígito é 0, não 2 (diferença: -2) 
        System.out.println (c.calcularE_metodo2());
    }
}

muito legal!

O estudo das séries de Taylor e MacLaurin são fundamentais para desenvolvimento de programas que trabalham com calculo pesado.

vc viu se essa é a formula conhecida que converge mais rápido?

Show mesmo entanglement! Como o regis_hideki já disse, com essas informações já dá para resolver tranquilo!

R

vi o tópico sobre matemática e resolvi falar de meu problema sobre fatoração e equação de 1 e 2 grau, está no meu site, com link para download em 4shared.com o site é www.raghy.net23.net está no link JAVA, o programa é o matemática java.

Aliás também coube aqui no anexo, envio para apreciação…
está em andamento (sempre está em andamento) , mas funciona tudo até então.

B

raghy:
vi o tópico sobre matemática e resolvi falar de meu problema sobre fatoração e equação de 1 e 2 grau, está no meu site, com link para download em 4shared.com o site é www.raghy.net23.net está no link JAVA, o programa é o matemática java.

Aliás também coube aqui no anexo, envio para apreciação…
está em andamento (sempre está em andamento) , mas funciona tudo até então.

Show também raghy, valeu!

JavaDreams

Experimenta dar uma olhada nos livros abaixo
mas somente nos capítulos que tratam do assunto:

Java Use a Cabeça
Jaca Como Programar

Não sou muito fã de matemática mas tive
uma boa base vendo os capítulos que falam
sobre tamanho de tipos e comparação entre tipos,
estouro/derramamento, dentre outra coisas.

Abraço.

Criado 29 de julho de 2013
Ultima resposta 21 de ago. de 2013
Respostas 11
Participantes 7