Esiste un metodo che calcola un fattoriale in Java?


105

Non l'ho ancora trovato. Ho dimenticato qualcosa? So che un metodo fattoriale è un programma di esempio comune per i principianti. Ma non sarebbe utile avere un'implementazione standard da riutilizzare? Potrei usare un tale metodo con i tipi standard (ad es. Int, long ...) e anche con BigInteger / BigDecimal.

Risposte:


26

Non credo che sarebbe utile avere una funzione di libreria per fattoriale. C'è una buona quantità di ricerca su implementazioni fattoriali efficienti. Ecco una manciata di implementazioni.


188
Perché non è utile avere una funzione di libreria per il fattoriale?
mezzanotte

17
Non molte persone hanno effettivamente bisogno di fattoriali in codice reale. Se lo fai, probabilmente stai eseguendo alcuni calcoli o statistiche avanzati, nel qual caso molto probabilmente utilizzerai già una libreria matematica con un'implementazione fattoriale specializzata.
mikera

3
La funzione gamma è molto utile, motivo per cui è inclusa nella libreria standard C ++.
Columbo

2
Mi sembra che @KarlthePagan significhi che non è utile avere una funzione di libreria standard per il fattoriale - è corretto?
dantiston

quindi ti chiederebbero al colloquio di vedere se puoi farlo da solo * il mio caso
moldavo

59

Apache Commons Math ha alcuni metodi fattoriali nella classe MathUtils .


1
Sì. Roba buona. C'è un'implementazione di fattoriale per numeri float e non flat (MathUtils.factorial (int) e MathUtils.factorialDouble (int)) e anche un utile logaritmo naturale di n! (MathUtils.factorialLog (int))
Tomasz Błachowicz

3
è in ArithmeticUtils al momento.
MarianP

6
ArithmeticUtils.factorial è apparentemente deprecato ora, gli atm usano CombinatoricsUtils.factorial
Victor Häggqvist

Per favore non usare link! Tendono a scomparire (come hanno fatto TUTTI questi).
SMBiggs

1
@ScottBiggs Entrambi i link nella risposta funzionano bene.
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;
    }
}

Versione Big Numbers di 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;
}

Inoltre stai assumendo di volere il fattoriale di un numero intero
Andrew

Questa soluzione dovrebbe utilizzare la classe BigInteger.
Oleg Abrazhaev

2
Versione Big Numbers: fattoriale BigInteger statico pubblico (BigInteger n) {Fattoriale BigInteger = BigInteger.valueOf (1); for (int i = 1; i <= n.intValue (); i ++) {factorial = factorial.multiply (BigInteger.valueOf (i)); } fattoriale di ritorno; }
HoldOffHunger

23

I fattoriali nudi nudi sono raramente necessari nella pratica. Molto spesso avrai bisogno di uno dei seguenti:

1) dividere un fattoriale per un altro, o

2) risposta in virgola mobile approssimata.

In entrambi i casi, staresti meglio con semplici soluzioni personalizzate.

Nel caso (1), diciamo, se x = 90! / 85 !, calcolerai il risultato esattamente come x = 86 * 87 * 88 * 89 * 90, senza bisogno di tenere 90! in memoria :)

Nel caso (2), google per "approssimazione di Stirling".


3
Controesempio: il calcolo del numero di permutazioni con N elementi richiede un semplice fattoriale, ed è necessario se vuoi allocare una struttura per contenere le permutazioni.
Mark Jeronimus

Buon punto! La mia prima domanda è stata se 90! / 85! semplificato per via di un denominatore comune di 5, ma in realtà era il denominatore comune di 85 !. 90! / 85! = 90 * 89 * 88 * 87 * 86 * 85! / 85 !. Puoi vederlo più chiaramente in quell'uguaglianza.
HoldOffHunger


6

Sebbene i fattoriali siano un buon esercizio per il programmatore principiante, non sono molto utili nella maggior parte dei casi e tutti sanno come scrivere una funzione fattoriale, quindi in genere non sono nella libreria media.


6
Sono d'accordo con te, ci sono funzioni matematiche più importanti. Ma ai miei occhi questo metodo dovrebbe essere standard in modo che le persone possano riutilizzarlo. Non è necessario implementarlo più volte da più persone. Per scopi educativi che possono fare. Ma per il lavoro quotidiano è obsoleto. Questa è la mia opinione. Comunque, grazie per aver risposto. Lo farò da solo, un'altra volta.

Qual è il vantaggio della standardizzazione proposta? L'aggiunta di metodi alla libreria standard non è gratuita. Come altri hanno sottolineato, non esiste un'unica soluzione migliore . Quale proponi di inserire nella lingua? Avere un metodo nella libreria standard non ti farà risparmiare il tempo necessario per comprendere il tuo problema e, una volta fatto, potresti anche selezionare l'implementazione più adatta per il lavoro.
Matt G,

2
"... e tutti sanno come scrivere una funzione fattoriale" chaosinmotion.com/blog/?p=622
James P.

4
Disaccordo. I fattoriali sono necessari per Combinatorics , che è necessario in molte aree della progettazione del software. L'argomento che non include i fattoriali in una libreria di matematica incorporata è lo stesso argomento di non avere una libreria di matematica incorporata.
LateralFractal

Che straordinario pezzo di logica. Assolutamente stellare. Peccato che i progettisti della classe java.lang.Math non lo sapessero quando hanno incluso i metodi abs () in quella libreria.
Igor Soudakevitch il

6

credo che questo sarebbe il modo più veloce, da una tabella di ricerca:

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];
}

Per il tipo nativo long(8 byte), può contenere solo fino a20!

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

Ovviamente, 21!causerà il trabocco.

Pertanto, per il tipo nativo long, 20!è consentito solo un massimo di , significativo e corretto.


1
Piuttosto bella idea. considerando che i primi 20 fattoriali sono probabilmente sufficienti, aggiungerei una costante statica (non è necessario calcolarla ogni volta che viene avviata l'app) alla classe Math con i dati forniti. Il fatto che non molte persone abbiano bisogno di fattoriali nel loro codice è una scusa scadente per non supportarlo nelle lezioni di matematica.
TG

6

Poiché il fattoriale cresce così rapidamente, lo stack overflow non è un problema se si utilizza la ricorsione. In effetti, il valore di 20! è il più grande che può rappresentare in un long Java. Quindi il metodo seguente calcolerà il fattoriale (n) o genererà un'eccezione IllegalArgumentException se n è troppo 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);
}

Un altro modo (più interessante) per fare le stesse cose è usare la libreria di flusso di Java 8 in questo modo:

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);
}

Ulteriori informazioni su Factorials utilizzando i flussi di Java 8



6

La risposta breve è: usa la ricorsione.

Puoi creare un metodo e chiamare quel metodo direttamente all'interno dello stesso metodo in modo ricorsivo:

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);
    }
}

5
le funzioni ricorsivamente sono belle, ma se qualcuno proverà a contare un fatiorial davvero grande, finirà con StackOverflowException;) + Non ne sono sicuro, ma penso che la ricorsione sia più lenta del buon vecchio metodo del ciclo;)
TG

come puoi sapere che finirà con l'eccezione StackOverflow? @TG
gumuruh

2
È semplice. ciascuno ricorsivamente sta mettendo la posizione corrente sullo stack in modo che il programma abbia la 'memoria' del posto in cui tornare dopo che la chiamata al metodo è terminata. Lo stack ha i suoi limiti. per provarlo per te stesso prova nel codice sopra cambia System.out.println(calc(10));per System.out.println(calc(Long.MAX_VALUE));dovresti ottenere una traccia stactrace piuttosto lunga :)
TG

@TG Per essere chiari ho provato il mio metodo ricorsivo con cui funziona BigInteger. Ho provato a calcolare il fattoriale del numero 8020che mi ha dato il risultato 613578884952214809325384...che ha 27831cifre decimali. Quindi, anche quando si lavora con numeri, Stackoverflowverrà generato un enorme no . Ovviamente hai ragione, ma dubito che ci siano numeri così grandi con un uso pratico :-)
Moritz Schmidt

3

Prova questo

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;
}

3
Credo che ci sia un bug nel ciclo for: dovrebbe essere i <= value. Il ciclo for potrebbe essere leggermente ottimizzato per (int i = 2; i <= value; i++).
Chris

2

Ho trovato un trucco straordinario per trovare fattoriali solo nella metà delle moltiplicazioni effettive.

Per favore sii paziente perché questo è un post un po 'lungo.

Per i numeri pari: per dimezzare la moltiplicazione con i numeri pari, ti ritroverai con n / 2 fattori. Il primo fattore sarà il numero di cui stai prendendo il fattoriale, quindi il prossimo sarà quel numero più quel numero meno due. Il numero successivo sarà il numero precedente più l'ultimo numero aggiunto meno due. Hai finito quando l'ultimo numero che hai aggiunto era due (cioè 2) . Probabilmente non aveva molto senso, quindi lascia che ti faccia un esempio.

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

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

Nota che ho iniziato con 8, quindi il primo numero che ho aggiunto è stato 6, quindi 4, quindi 2, ogni numero aggiunto è due in meno del numero aggiunto prima di esso. Questo metodo equivale a moltiplicare i numeri minimi con i numeri maggiori, solo con meno moltiplicazione, in questo modo:

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

Semplice non è vero :)

Ora per i numeri dispari: se il numero è dispari, l'aggiunta è la stessa, poiché sottrai due ogni volta, ma ti fermi a tre. Il numero di fattori tuttavia cambia. Se dividi il numero per due, finirai con un numero che termina con .5. Il motivo è che se moltiplichiamo le estremità insieme, ci rimane il numero medio. Fondamentalmente, tutto questo può essere risolto risolvendo per un numero di fattori pari al numero diviso per due, arrotondato per eccesso. Probabilmente questo non aveva molto senso neanche per menti senza un background matematico, quindi fammi fare un esempio:

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

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

Nota: se non ti piace questo metodo, potresti anche prendere il fattoriale del numero pari prima del numero dispari (otto in questo caso) e moltiplicarlo per il numero dispari (cioè 9! = 8! * 9).

Ora implementiamolo in 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è una variabile booleana dichiarata come statica; viene utilizzato per il 1 ° caso in cui non si vuole modificare la somma precedente.

Prova con numeri pari e dispari.


2

Puoi usare la ricorsione.

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

e poi dopo aver creato il metodo (funzione) sopra:

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

1

L'unico uso aziendale per un fattoriale a cui riesco a pensare sono le formule Erlang B ed Erlang C, e non tutti lavorano in un call center o per la compagnia telefonica. L'utilità di una funzionalità per il business sembra spesso dettare ciò che viene visualizzato in una lingua: guarda tutte le funzioni di gestione dei dati, XML e web nelle principali lingue.

È facile mantenere uno snippet fattoriale o una funzione di libreria per qualcosa di simile in giro.


1

Un metodo molto semplice per calcolare i fattoriali:

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;
}

Ho usato double perché possono contenere numeri enormi, ma puoi usare qualsiasi altro tipo come int, long, float, ecc.

PS Questa potrebbe non essere la soluzione migliore, ma sono nuovo nella codifica e mi ci sono voluti anni per trovare un codice semplice che potesse calcolare i fattoriali, quindi ho dovuto scrivere il metodo da solo ma lo sto mettendo qui in modo che aiuti altre persone come me .


1

Puoi anche usare la versione ricorsiva.

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

La ricorsione è solitamente meno efficiente a causa della necessità di eseguire il push e il pop delle ricorsioni, quindi l'iterazione è più veloce. D'altra parte, le versioni ricorsive utilizzano meno o nessuna variabile locale, il che è vantaggioso.


1

Factorial è una funzione discreta in forte aumento, quindi penso che usare BigInteger sia meglio che usare int. Ho implementato il seguente codice per il calcolo del fattoriale di interi non negativi. Ho usato la ricorsione al posto di un ciclo.

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")))); 
}

Qui la gamma di numeri interi grandi è

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

Tuttavia, la gamma del metodo fattoriale indicato sopra può essere estesa fino a due volte utilizzando BigInteger senza segno.


1

Abbiamo una sola riga per calcolarlo:

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

1

Un metodo abbastanza semplice

    for ( int i = 1; i < n ; i++ )
    {
            answer = answer * i;
    }

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);
}
}

0
public static int fact(int i){
    if(i==0)
       return 0;
    if(i>1){
       i = i * fact(--i);
    }

   return i;
}

1
Penso che l'OP chieda se c'è una funzione nelle API, non come scriverne una. Inoltre, 0! = 1 - potresti voler aggiornare il tuo codice per includere quel caso.
SL Barth - Ripristina Monica

risulterà sempre 0
Tom Brito

0

Dobbiamo implementarlo in modo iterativo. Se implementiamo ricorsivamente, causerà StackOverflow se l'input diventa molto grande (cioè 2 miliardi). E abbiamo bisogno di utilizzare un numero di dimensione non associato come BigInteger per evitare un overflow aritmatico quando un numero fattoriale diventa più grande del numero massimo di un dato tipo (cioè 2 miliardi per int). Puoi usare int per un massimo di 14 di fattoriale e long per un massimo di 20 di fattoriale prima dell'overflow.

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 non puoi utilizzare BigInteger, aggiungi un controllo degli errori.

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;
}

0
public int factorial(int num) {
        if (num == 1) return 1;
        return num * factorial(num - 1);
}

Dovrebbe usare long o BigInteger;)
AxelH

0

ciclo while (per piccoli numeri)

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);
   }
}

0

L'ho preso da EDX, usalo! si chiama ricorsione

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

0

con ricorsione:

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

con ciclo while:

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

0

L'USO DELLA PROGRAMMAZIONE DINAMICA È EFFICIENTE

se vuoi usarlo per calcolare ancora e ancora (come il caching)

Codice 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];
 }

0

usare la ricorsione è il metodo più semplice. se vogliamo trovare il fattoriale di N, dobbiamo considerare i due casi in cui N = 1 e N> 1 poiché in fattoriale continuiamo a moltiplicare N, N-1, N-2 ,,,,, fino a 1. se abbiamo vai a N = 0 otterremo 0 per la risposta. per impedire che il fattoriale raggiunga lo zero, viene utilizzato il seguente metodo ricorsivo. All'interno della funzione fattoriale, mentre N> 1, il valore di ritorno viene moltiplicato per un'altra iniziazione della funzione fattoriale. questo manterrà il codice che chiama ricorsivamente il fattoriale () finché non raggiunge N = 1. per il caso N = 1, restituisce N (= 1) stesso e tutto il risultato precedentemente costruito del ritorno moltiplicato N s viene moltiplicato per N = 1. Così dà il risultato fattoriale.

static int factorial(int N) {
    if(N > 1) { 
    return n * factorial(N - 1);
    }
    // Base Case N = 1
    else { 
    return N;
    }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.