Existe um método que calcula um fatorial em Java?

105

Eu não encontrei ainda. Perdi alguma coisa? Eu sei que um método fatorial é um programa de exemplo comum para iniciantes. Mas não seria útil ter uma implementação padrão para esta reutilizar? Eu poderia usar esse método com tipos padrão (Ex. Int, long ...) e com BigInteger / BigDecimal também.

Leomord
fonte

Respostas:

26

Não acho que seria útil ter uma função de biblioteca para fatorial. Existem muitas pesquisas sobre implementações fatoriais eficientes. Aqui está um punhado de implementações.

Carl o pagão
fonte
188
Por que não é útil ter uma função de biblioteca para fatorial?
meia
17
Poucas pessoas realmente precisam de fatoriais em código real. Se você fizer isso, provavelmente estará fazendo alguma matemática ou estatística avançada; nesse caso, provavelmente já estará usando uma biblioteca matemática com uma implementação fatorial especializada.
mikera
3
A função gama é muito útil, por isso está incluída na biblioteca padrão C ++.
Columbo
2
Parece-me que @KarlthePagan significa que não é útil ter uma função de biblioteca padrão para fatorial - correto?
dantiston
então eles pediriam a você na entrevista para ver se você pode fazer isso sozinho * meu caso
moldavo
59

O Apache Commons Math possui alguns métodos fatoriais na classe MathUtils .

Bill the Lizard
fonte
1
Sim. Coisa boa. Há uma implementação de fatorial para números flutuantes e não planos (MathUtils.factorial (int) e MathUtils.factorialDouble (int)) e também o logaritmo natural útil de n! (MathUtils.factorialLog (int))
Tomasz Błachowicz
3
está no ArithmeticUtils no momento.
MarianP
6
ArithmeticUtils.factorial está aparentemente obsoleto agora, atm use CombinatoricsUtils.factorial
Victor Häggqvist
Por favor, não use links! Eles tendem a desaparecer (como TODOS estes).
SMBiggs
1
@ScottBiggs Ambos os links na resposta estão funcionando bem.
Bill the Lizard
40
public class UsefulMethods {
    public static long factorial(int number) {
        long result = 1;

        for (int factor = 2; factor <= number; factor++) {
            result *= factor;
        }

        return result;
    }
}

Versão do Big Numbers por HoldOffHunger :

public static BigInteger factorial(BigInteger number) {
    BigInteger result = BigInteger.valueOf(1);

    for (long factor = 2; factor <= number.longValue(); factor++) {
        result = result.multiply(BigInteger.valueOf(factor));
    }

    return result;
}
saroj adhikari
fonte
Além disso, você está assumindo que deseja o fatorial de um inteiro
Andrew
Esta solução deve usar a classe BigInteger.
Oleg Abrazhaev
2
Versão dos Big Numbers: public static BigInteger factorial (BigInteger n) {BigInteger factorial = BigInteger.valueOf (1); para (int i = 1; i <= n.intValue (); i ++) {factorial = factorial.multiply (BigInteger.valueOf (i)); } return fatorial; }
HoldOffHunger
23

Fatoriais nus raramente são necessários na prática. Na maioria das vezes, você precisará de um dos seguintes:

1) dividir um fatorial por outro, ou

2) resposta aproximada de ponto flutuante.

Em ambos os casos, você ficaria melhor com soluções personalizadas simples.

No caso (1), diga, se x = 90! / 85 !, então você calculará o resultado da mesma maneira que x = 86 * 87 * 88 * 89 * 90, sem a necessidade de segurar 90! em memória :)

No caso (2), procure no Google por "aproximação de Stirling".

Igor Krivokon
fonte
3
Contra-exemplo: Calcular o número de permutações com N elementos requer fatorial simples e é necessário se você deseja alocar uma estrutura para conter as permutações.
Mark Jeronimus
Bom ponto! Minha primeira pergunta foi se 90! / 85! simplificado por causa de um denominador comum de 5, mas na verdade, era o denominador comum de 85 !. 90! / 85! = 90 * 89 * 88 * 87 * 86 * 85! / 85 !. Você pode ver isso mais claramente nessa igualdade.
HoldOffHunger
12

Use a goiaba da BigIntegerMathseguinte maneira:

BigInteger factorial = BigIntegerMath.factorial(n);

(Funcionalidades semelhantes para inte longestão disponíveis em IntMathe LongMathrespectivamente.)

dogbane
fonte
6

Embora os fatoriais sejam um bom exercício para o programador iniciante, eles não são muito úteis na maioria dos casos, e todos sabem como escrever uma função fatorial, portanto, normalmente não estão na biblioteca comum.

bdonlan
fonte
6
Eu concordo com você, existem funções matemáticas mais importantes. Mas, a meu ver, esse método deve ser padrão para que as pessoas possam reutilizá-lo. Não há necessidade de implementá-lo várias vezes por várias pessoas. Isso pode servir para fins educacionais. Mas, para o trabalho diário, está obsoleto. Esta é a minha opinião. De qualquer forma, obrigado por responder. Eu farei isso sozinho - em outra hora.
Qual é o benefício de sua padronização proposta? Adicionar métodos à biblioteca padrão tem custos. Como outros apontaram, não existe uma única solução melhor . Qual você propõe construir na linguagem? Ter um método na biblioteca padrão não vai economizar tempo para entender seu problema e, depois de fazer isso, você também pode selecionar a implementação mais adequada para o trabalho.
Matt G
2
"... e todo mundo sabe como escrever uma função fatorial" chaosinmotion.com/blog/?p=622
James P.
4
Discordo. Os fatoriais são necessários para a Combinatória , que é necessária em muitas áreas do design de software. O argumento para não incluir fatoriais em uma biblioteca matemática integrada é o mesmo argumento que não ter uma biblioteca matemática integrada.
LateralFractal
Que peça de lógica notável. Absolutamente estelar. Uma pena que os designers da classe java.lang.Math ignoraram isso quando incluíram os métodos abs () nessa biblioteca.
Igor Soudakevitch
6

acredito que essa seria a maneira mais rápida, por meio de uma tabela de consulta:

private static final long[] FACTORIAL_TABLE = initFactorialTable();
private static long[] initFactorialTable() {
    final long[] factorialTable = new long[21];
    factorialTable[0] = 1;
    for (int i=1; i<factorialTable.length; i++)
        factorialTable[i] = factorialTable[i-1] * i;
    return factorialTable;
}
/**
 * Actually, even for {@code long}, it works only until 20 inclusively.
 */
public static long factorial(final int n) {
    if ((n < 0) || (n > 20))
        throw new OutOfRangeException("n", 0, 20);
    return FACTORIAL_TABLE[n];
}

Para o tipo nativo long(8 bytes), ele só pode conter até20!

20! = 2432902008176640000(10) = 0x 21C3 677C 82B4 0000

Obviamente, 21!causará estouro.

Portanto, para o tipo nativo long, apenas um máximo de 20!é permitido, significativo e correto.

meia-noite
fonte
1
Boa ideia. considerando que os primeiros 20 fatoriais provavelmente são suficientes, eu adicionaria constantes estáticas (não há necessidade de calculá-las toda vez que o aplicativo for iniciado) à classe de matemática com os dados fornecidos. O fato de que muitas pessoas não precisam de fatoriais em seu código é uma péssima desculpa para não suportá-lo na aula de matemática.
TG de
6

Como o fatorial cresce muito rapidamente, o estouro de pilha não é um problema se você usar a recursão. Na verdade, o valor de 20! é o maior que se pode representar em um Java long. Portanto, o método a seguir calculará o fatorial (n) ou lançará uma IllegalArgumentException se n for muito grande.

public long factorial(int n) {
    if (n > 20) throw new IllegalArgumentException(n + " is out of range");
    return (1 > n) ? 1 : n * factorial(n - 1);
}

Outra maneira (mais legal) de fazer as mesmas coisas é usar a biblioteca de fluxo do Java 8 assim:

public long factorial(int n) {
    if (n > 20) throw new IllegalArgumentException(n + " is out of range");        
    return LongStream.rangeClosed(1, n).reduce(1, (a, b) -> a * b);
}

Leia mais sobre fatoriais usando fluxos do Java 8

Per-Åke Minborg
fonte
6

O pacote Apache Commons Math tem um método fatorial , acho que você poderia usá-lo.

Valentin Rocher
fonte
O link atualizado é este
Marco Lackovic
@Krige acabou de consertar o link
fedorqui 'SO pare de prejudicar'
6

A resposta curta é: use recursão.

Você pode criar um método e chamar esse método dentro do mesmo método recursivamente:

public class factorial {

    public static void main(String[] args) {
        System.out.println(calc(10));
    }

    public static long calc(long n) {
        if (n <= 1)
            return 1;
        else
            return n * calc(n - 1);
    }
}
Feri
fonte
5
funções recursivas são boas, mas se alguém tentar contar um fatiorial realmente grande, acabará com StackOverflowException;) + Não tenho certeza, mas acho que a recursão é mais lenta do que o bom e velho método de loop;)
TG
como você pode saber que vai acabar com a exceção stackoverflow? @TG
gumuruh
2
Isso é simples. cada um recursivamente está colocando o local atual na pilha, de modo que o programa terá 'memória' do local para onde voltar após a chamada do método terminar. Stack tem seus limites. para tentar por si mesmo, tente no código acima, mude System.out.println(calc(10));para System.out.println(calc(Long.MAX_VALUE));você deve obter stactrace bem longo :)
TG
@TG Para ficar claro, tentei meu método recursivo com o qual está trabalhando BigInteger. Tentei calcular o fatorial do número 8020que me deu o resultado 613578884952214809325384...que tem 27831casas decimais. Portanto, mesmo quando se trabalha com números, esse grande não Stackoverflowserá lançado. Claro que você está certo, mas eu duvido que haja números tão grandes assim com um uso prático :-)
Moritz Schmidt
3

Tente isto

public static BigInteger factorial(int value){
    if(value < 0){
        throw new IllegalArgumentException("Value must be positive");
    }

    BigInteger result = BigInteger.ONE;
    for (int i = 2; i <= value; i++) {
        result = result.multiply(BigInteger.valueOf(i));
    }

    return result;
}
Ilya Gazman
fonte
3
Acredito que haja um bug no loop for: deveria ser i <= value. O loop for pode ser ligeiramente otimizado para (int i = 2; i <= value; i++).
Chris
2

Eu descobri um truque incrível para encontrar fatoriais em apenas metade das multiplicações reais.

Por favor, seja paciente, pois esta é uma postagem um pouco longa.

Para números pares : Para reduzir pela metade a multiplicação com números pares, você acabará com n / 2 fatores. O primeiro fator será o número do qual você está tirando o fatorial, então o próximo será aquele número mais aquele número menos dois. O próximo número será o número anterior mais o último número adicionado menos dois. Você terá terminado quando o último número adicionado for dois (ou seja, 2) . Isso provavelmente não fazia muito sentido, então deixe-me dar um exemplo.

8! = 8 * (8 + 6 = 14) * (14 + 4 = 18) * (18 + 2 = 20)

8! = 8 * 14 * 18 * 20 which is **40320** 

Observe que comecei com 8, depois o primeiro número que adicionei foi 6, depois 4 e 2, cada número adicionado sendo dois a menos que o número adicionado antes dele. Este método equivale a multiplicar o menor número pelos maiores números, apenas com menos multiplicação, assim:

8! = 1 * 2 * 3 * 4 * 5 * 6 * 7 * 
8! = (1 * 8) * (2 * 7) * (3 * 6) * (4 * 5)
8! = 8 * 14 * 18 * 20

Simples, não é :)

Agora, para números ímpares: Se o número for ímpar, a soma é a mesma, já que você subtrai dois de cada vez, mas pára no três. O número de fatores, entretanto, muda. Se você dividir o número por dois, acabará com algum número terminando em 0,5. A razão é que, se multiplicarmos as pontas, ficamos com o número do meio. Basicamente, tudo isso pode ser resolvido resolvendo para um número de fatores igual ao número dividido por dois, arredondado para cima. Isso provavelmente não fazia muito sentido para mentes sem formação matemática, então deixe-me dar um exemplo:

9! = 9 * (9 + 7 = 16) * (16 + 5 = 21) * (21 + 3 = 24) * (roundUp(9/2) = 5)

9! = 9 * 16 * 21 * 24 * 5 = **362880**

Nota: Se você não gostar deste método, você também pode simplesmente pegar o fatorial do número par antes do ímpar (oito neste caso) e multiplicá-lo pelo número ímpar (ou seja, 9! = 8! * 9).

Agora vamos implementá-lo em Java:

public static int getFactorial(int num)
{
    int factorial=1;
    int diffrennceFromActualNum=0;
    int previousSum=num;

    if(num==0) //Returning  1 as factorial if number is 0 
        return 1;
    if(num%2==0)//  Checking if Number is odd or even
    { 
        while(num-diffrennceFromActualNum>=2)
        {
            if(!isFirst)
            {
                previousSum=previousSum+(num-diffrennceFromActualNum);  
            }
            isFirst=false;
            factorial*=previousSum;
            diffrennceFromActualNum+=2;
        }
    }
    else // In Odd Case (Number * getFactorial(Number-1))
    {
        factorial=num*getFactorial(num-1);
    }
    return factorial;
}

isFirsté uma variável booleana declarada como estática; é usado para o primeiro caso em que não queremos alterar a soma anterior.

Experimente com números pares e ímpares.

Neeraj Jain
fonte
2

Você pode usar recursão.

public static int factorial(int n){    
      if (n == 0)    
        return 1;    
      else    
        return(n * factorial(n-1));    
     }

e depois de criar o método (função) acima:

System.out.println(factorial(number of your choice));  
    //direct example
    System.out.println(factorial(3));
Cristian Babarusi
fonte
1

O único uso comercial para um fatorial que consigo pensar são as fórmulas Erlang B e Erlang C, e nem todo mundo trabalha em um call center ou para a companhia telefônica. A utilidade de um recurso para negócios parece frequentemente ditar o que aparece em uma linguagem - observe todo o manuseio de dados, XML e funções da web nas principais linguagens.

É fácil manter um fragmento fatorial ou função de biblioteca para algo assim.

R Ubben
fonte
1

Um método muito simples para calcular fatoriais:

private double FACT(double n) {
    double num = n;
    double total = 1;
    if(num != 0 | num != 1){
        total = num;
    }else if(num == 1 | num == 0){
        total = 1;
    }
    double num2;
    while(num > 1){
        num2 = num - 1;
        total = total * num2;
        num = num - 1;
    }
    return total;
}

Usei double porque eles podem conter números enormes, mas você pode usar qualquer outro tipo, como int, long, float, etc.

PS Esta pode não ser a melhor solução, mas eu sou novo em programação e levei muito tempo para encontrar um código simples que pudesse calcular fatoriais, então eu tive que escrever o método sozinho, mas estou colocando isso aqui para ajudar outras pessoas como eu .

Kaamil Jasani
fonte
1

Você também pode usar a versão de recursão.

static int myFactorial(int i) {
    if(i == 1)
        return;
    else
        System.out.prinln(i * (myFactorial(--i)));
}

A recursão geralmente é menos eficiente devido à necessidade de enviar e fazer pop recursões, de modo que a iteração é mais rápida. Por outro lado, as versões recursivas usam menos ou nenhuma variável local, o que é uma vantagem.

Hesam
fonte
1

Fatorial é uma função discreta altamente crescente. Portanto, acho que usar BigInteger é melhor do que usar int. Implementei o código a seguir para o cálculo do fatorial de inteiros não negativos. Usei a recursão em vez de usar um loop.

public  BigInteger factorial(BigInteger x){     
    if(x.compareTo(new BigInteger("1"))==0||x.compareTo(new BigInteger("0"))==0)
        return new BigInteger("1");
    else return x.multiply(factorial(x.subtract(new BigInteger("1")))); 
}

Aqui, o intervalo de inteiro grande é

-2^Integer.MAX_VALUE (exclusive) to +2^Integer.MAX_VALUE,
where Integer.MAX_VALUE=2^31.

No entanto, o intervalo do método fatorial fornecido acima pode ser estendido até duas vezes usando BigInteger não assinado.

Sanjeet A
fonte
1

Temos uma única linha para calculá-lo:

Long factorialNumber = LongStream.rangeClosed(2, N).reduce(1, Math::multiplyExact);
krmanish007
fonte
1

Um método bastante simples

    for ( int i = 1; i < n ; i++ )
    {
            answer = answer * i;
    }
DalekCaan99
fonte
1
    /**
import java liberary class

*/
import java.util.Scanner;

/* class to find factorial of a number
*/

public class factorial
{
public static void main(String[] args)
{

// scanner method for read keayboard values

    Scanner factor= new Scanner(System.in);

    int n;
    double total = 1;
    double sum= 1;

    System.out.println("\nPlease enter an integer: ");
    n = factor.nextInt();

// evaluvate the integer is greater than zero and calculate factorial

if(n==0)

{
    System.out.println(" Factorial of 0 is 1");
}
else if (n>0)
{
    System.out.println("\nThe factorial of " + n + " is " );

    System.out.print(n);

    for(int i=1;i<n;i++)
    {
        do // do while loop for display each integer in the factorial
              {
                System.out.print("*"+(n-i) );
              }

        while ( n == 1);

      total = total * i;

    }

// calculate factorial
sum= total * n;


// display sum of factorial

    System.out.println("\n\nThe "+ n +" Factorial is : "+" "+ sum);
}

// display invalid entry, if enter a value less than zero

else

{
    System.out.println("\nInvalid entry!!");

}System.exit(0);
}
}
jith009
fonte
0
public static int fact(int i){
    if(i==0)
       return 0;
    if(i>1){
       i = i * fact(--i);
    }

   return i;
}
Jaikrat
fonte
1
Acho que o OP está perguntando se há uma função na API, não como escrever uma. Além disso, 0! = 1 - você pode desejar atualizar seu código para incluir esse caso.
SL Barth - Reintegração de Monica
resultará sempre 0
Tom Brito
0

Precisamos implementar iterativamente. Se implementarmos recursivamente, isso causará StackOverflow se a entrada se tornar muito grande (ou seja, 2 bilhões). E precisamos usar o número de tamanho não acoplado, como BigInteger para evitar um estouro aritmático quando um número fatorial se torna maior do que o número máximo de um determinado tipo (ou seja, 2 bilhões para int). Você pode usar int para no máximo 14 de fatorial e long para no máximo 20 de fatorial antes do estouro.

public BigInteger getFactorialIteratively(BigInteger input) {
    if (input.compareTo(BigInteger.ZERO) <= 0) {
        throw new IllegalArgumentException("zero or negatives are not allowed");
    }

    BigInteger result = BigInteger.ONE;
    for (BigInteger i = BigInteger.ONE; i.compareTo(input) <= 0; i = i.add(BigInteger.ONE)) {
        result = result.multiply(i);
    }
    return result;
}

Se você não puder usar BigInteger, adicione uma verificação de erro.

public long getFactorialIteratively(long input) {
    if (input <= 0) {
        throw new IllegalArgumentException("zero or negatives are not allowed");
    } else if (input == 1) {
        return 1;
    }

    long prev = 1;
    long result = 0;
    for (long i = 2; i <= input; i++) {
        result = prev * i;
        if (result / prev != i) { // check if result holds the definition of factorial
            // arithmatic overflow, error out
            throw new RuntimeException("value "+i+" is too big to calculate a factorial, prev:"+prev+", current:"+result);
        }
        prev = result;
    }
    return result;
}
joohwan
fonte
0
public int factorial(int num) {
        if (num == 1) return 1;
        return num * factorial(num - 1);
}
Scott Zhu
fonte
Deve usar long ou BigInteger;)
AxelH
0

loop while (para números pequenos)

public class factorial {

public static void main(String[] args) {
    int counter=1, sum=1;

    while (counter<=10) {
        sum=sum*counter;
        counter++;
   }

    System.out.println("Factorial of 10 is " +sum);
   }
}
Dejanmarich
fonte
0

Peguei isso do EDX use-o! é chamado de recursão

   public static int factorial(int n) {
    if (n == 1) {
        return 1;
    } else {
        return n * factorial(n-1);
    }
}
Rick
fonte
0

com recursão:

public static int factorial(int n)
{
    if(n == 1)
    {
        return 1;
    }               
    return n * factorial(n-1);
}

com loop while:

public static int factorial1(int n)
{
    int fact=1;
    while(n>=1)
    {
        fact=fact*n;
        n--;
    }
    return fact;
}
Niteesh Gupta
fonte
0

USAR A PROGRAMAÇÃO DINÂMICA É EFICIENTE

se você quiser usá-lo para calcular repetidamente (como cache)

Código Java:

int fact[]=new int[n+1]; //n is the required number you want to find factorial for.
int factorial(int num)
 {
    if(num==0){
     fact[num]=1;
     return fact[num];
       }
     else
       fact[num]=(num)*factorial(num-1);

     return fact[num];
 }
Ganesh Chowdhary Sadanala
fonte
0

usar recursão é o método mais simples. se quisermos encontrar o fatorial de N, temos que considerar os dois casos onde N = 1 e N> 1 já que no fatorial continuamos multiplicando N, N-1, N-2 ,,,,,, até 1. se nós vá para N = 0 obteremos 0 para a resposta. para impedir que o fatorial chegue a zero, é usado o seguinte método recursivo. Dentro da função fatorial, enquanto N> 1, o valor de retorno é multiplicado com outra iniciação da função fatorial. isso irá manter o código chamando recursivamente o fatorial () até atingir N = 1. para o caso N = 1, ele retorna N (= 1) e todo o resultado previamente construído do retorno multiplicado N s é multiplicado por N = 1. Assim, dá o resultado fatorial.

static int factorial(int N) {
    if(N > 1) { 
    return n * factorial(N - 1);
    }
    // Base Case N = 1
    else { 
    return N;
    }
Vishaka Basnayake
fonte