¿Existe algún método que calcule un factorial en Java?

105

No lo encontré todavía. ¿Me he perdido algo? Sé que un método factorial es un programa de ejemplo común para principiantes. ¿Pero no sería útil tener una implementación estándar para que esta se pueda reutilizar? Podría usar un método de este tipo con tipos estándar (por ejemplo, int, long ...) y también con BigInteger / BigDecimal.

Leomord
fuente

Respuestas:

26

No creo que sea útil tener una función de biblioteca para factorial. Hay mucha investigación sobre implementaciones factoriales eficientes. Aquí hay algunas implementaciones.

Karl el pagano
fuente
188
¿Por qué no es útil tener una función de biblioteca para factorial?
midnite
17
No mucha gente necesita factoriales en código real. Si lo hace, probablemente esté haciendo algunas matemáticas o estadísticas avanzadas, en cuyo caso probablemente ya estará usando una biblioteca matemática con una implementación factorial especializada.
mikera
3
La función gamma es muy útil, por lo que está incluida en la biblioteca estándar de C ++.
Columbo
2
Me parece que @KarlthePagan significa que no es útil tener una función de biblioteca estándar para factorial, ¿es correcto?
dantiston
para que te pregunten en la entrevista para ver si puedes hacerlo tú mismo * mi caso
moldovean
59

Apache Commons Math tiene algunos métodos factoriales en la clase MathUtils .

Bill el lagarto
fuente
1
Sí. Buen material. Hay una implementación de factorial para números flotantes y no planos (MathUtils.factorial (int) y MathUtils.factorialDouble (int)) y también logaritmo natural útil de n! (MathUtils.factorialLog (int))
Tomasz Błachowicz
3
está en ArithmeticUtils en este momento.
MarianP
6
ArithmeticUtils.factorial aparentemente está obsoleto ahora, atm use CombinatoricsUtils.factorial
Victor Häggqvist
¡No utilices enlaces! Tienden a desaparecer (como lo han hecho TODOS).
SMBiggs
1
@ScottBiggs Ambos enlaces en la respuesta funcionan bien.
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;
    }
}

Versión de Big Numbers de 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
fuente
También está asumiendo que desea el factorial de un entero
Andrew
Esta solución debería usar la clase BigInteger.
Oleg Abrazhaev
2
Versión de números grandes: público estático BigInteger factorial (BigInteger n) {BigInteger factorial = BigInteger.valueOf (1); for (int i = 1; i <= n.intValue (); i ++) {factorial = factorial.multiply (BigInteger.valueOf (i)); } retorno factorial; }
HoldOffHunger
23

En la práctica, rara vez se necesitan factoriales desnudos. La mayoría de las veces necesitará uno de los siguientes:

1) dividir un factorial por otro, o

2) respuesta aproximada de punto flotante.

En ambos casos, estaría mejor con soluciones personalizadas simples.

En el caso (1), digamos, si x = 90! / 85 !, entonces calcularás el resultado como x = 86 * 87 * 88 * 89 * 90, ¡sin necesidad de mantener 90! en memoria :)

En el caso (2), busque en Google "aproximación de Stirling".

Igor Krivokon
fuente
3
Contraejemplo: calcular el número de permutaciones con N elementos requiere factorial simple y es necesario si desea asignar una estructura para contener las permutaciones.
Mark Jeronimus
¡Buen punto! Mi primera pregunta fue si 90! / 85! simplificado debido a un denominador común de 5, pero en realidad, ¡era el denominador común de 85 !. 90! / 85! = 90 * 89 * 88 * 87 * 86 * 85! / 85 !. Puedes verlo más claramente en esa igualdad.
HoldOffHunger
12

Use Guayaba de la BigIntegerMathsiguiente manera:

BigInteger factorial = BigIntegerMath.factorial(n);

(Funcionalidad similar para inty longestá disponible en IntMathy LongMathrespectivamente).

dogbane
fuente
6

Aunque los factoriales son un buen ejercicio para el programador principiante, no son muy útiles en la mayoría de los casos y todos saben cómo escribir una función factorial, por lo que normalmente no se encuentran en la biblioteca promedio.

bdonlan
fuente
6
Estoy de acuerdo contigo, hay funciones matemáticas más importantes. Pero en mi opinión, este método debería ser estándar para que la gente pueda reutilizarlo. No es necesario implementarlo varias veces por varias personas. Para fines educativos que pueden hacer. Pero para el trabajo diario es obsoleto. Esta es mi opinión. De todos modos, gracias por responder. Lo haré por mi cuenta, en otro momento.
¿Cuál es el beneficio de su estandarización propuesta? Agregar métodos a la biblioteca estándar no está exento de costos. Como han señalado otros, no existe una única solución óptima . ¿Cuál propones incorporar al idioma? Tener un método en la biblioteca estándar no le ahorrará el tiempo de comprender su problema, y ​​una vez que lo haya hecho, también puede seleccionar la implementación que mejor se adapte al trabajo.
Matt G
2
"... y todos saben cómo escribir una función factorial" chaosinmotion.com/blog/?p=622
James P.
4
Discrepar. Los factoriales son necesarios para Combinatoria , que se necesita en muchas áreas del diseño de software. El argumento de no incluir factoriales en una biblioteca de matemáticas incorporada es el mismo argumento que no tener una biblioteca de matemáticas incorporada.
LateralFractal
Qué pieza de lógica más sobresaliente. Absolutamente estelar. Lástima que los diseñadores de la clase java.lang.Math lo ignoraran cuando incluyeron los métodos abs () en esa biblioteca.
Igor Soudakevitch
6

Creo que esta sería la forma más rápida, mediante una tabla de búsqueda:

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 el tipo nativo long(8 bytes), solo puede contener hasta20!

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

Obviamente, 21!provocará un desbordamiento.

Por lo tanto, para el tipo nativo long, solo 20!se permite un máximo de , significativo y correcto.

midnite
fuente
1
Muy buena idea. teniendo en cuenta que los primeros 20 factoriales son probablemente suficientes, agregaría constantes estáticas (no es necesario calcularlas cada vez que se inicia la aplicación) a la clase de matemáticas con esos datos proporcionados. El hecho de que no mucha gente necesite factoriales en su código es una mala excusa para no admitirlo en la clase de matemáticas.
TG
6

Debido a que el factorial crece tan rápido, el desbordamiento de la pila no es un problema si usa la recursividad. De hecho, ¡el valor de 20! es el más grande que se puede representar en un largo de Java. Entonces, el siguiente método calculará factorial (n) o lanzará una IllegalArgumentException si n es demasiado 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);
}

Otra forma (más genial) de hacer lo mismo es usar la biblioteca de flujo de Java 8 de esta manera:

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

Leer más sobre Factorials usando los flujos de Java 8

Per-Åke Minborg
fuente
6

El paquete Apache Commons Math tiene un método factorial , creo que podrías usarlo.

Valentin Rocher
fuente
El enlace actualizado es este
Marco Lackovic
@Krige acaba de arreglar el enlace
fedorqui 'Así que deja de dañar'
6

La respuesta corta es: usa la recursividad.

Puede crear un método y llamar a ese método dentro del mismo método de forma recursiva:

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
fuente
5
Las funciones recursivas son agradables, pero si alguien intenta contar un fatiorial realmente grande, terminarán con StackOverflowException;) + No estoy seguro, pero creo que la recursividad es más lenta que el buen método de bucle antiguo;)
TG
¿Cómo puedes saber que terminará con la excepción de stackoverflow? @TG
gumuruh
2
Eso es simple. cada uno de forma recursiva está colocando el lugar actual en la pila para que el programa tenga 'memoria' de lugar al que volver después de que finalice la llamada al método. Stack tiene sus límites. para probarlo usted mismo, intente en el código anterior, cambie System.out.println(calc(10));a System.out.println(calc(Long.MAX_VALUE));que debería obtener una raza bastante larga :)
TG
@TG Para que quede claro, probé mi método recursivo con el que está funcionando BigInteger. Traté de calcular el factorial del número 8020que me dio el resultado 613578884952214809325384...que tiene 27831decimales. Entonces, incluso cuando se trabaja con números Stackoverflow, se arrojará un enorme no . Por supuesto que tienes razón, pero dudo que haya números tan grandes con un uso práctico :-)
Moritz Schmidt
3

Prueba esto

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
fuente
3
Creo que hay un error en el bucle for: debería serlo i <= value. El bucle for podría optimizarse ligeramente para (int i = 2; i <= value; i++).
Chris
2

Encontré un truco increíble para encontrar factoriales en solo la mitad de las multiplicaciones reales.

Tenga paciencia, ya que esta es una publicación un poco larga.

Para números pares: para dividir a la mitad la multiplicación con números pares, terminará con n / 2 factores. El primer factor será el número del que estás tomando el factorial, luego el siguiente será ese número más ese número menos dos. El siguiente número será el número anterior más el último número agregado menos dos. Ha terminado cuando el último número que agregó fue dos (es decir, 2) . Eso probablemente no tuvo mucho sentido, así que déjame darte un ejemplo.

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

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

Tenga en cuenta que comencé con 8, luego el primer número que agregué fue 6, luego 4, luego 2, cada número agregado es dos menos que el número agregado antes. Este método es equivalente a multiplicar los números más pequeños por los números más grandes, solo que con menos multiplicación, así:

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

Simple, ¿no es así?

Ahora para números impares: si el número es impar, la suma es la misma, ya que resta dos cada vez, pero se detiene en tres. Sin embargo, el número de factores cambia. Si divide el número por dos, terminará con un número que termine en .5. La razón es que si multiplicamos los extremos juntos, nos quedamos con el número del medio. Básicamente, todo esto se puede resolver resolviendo un número de factores igual al número dividido por dos, redondeado hacia arriba. Esto probablemente tampoco tenía mucho sentido para las mentes sin conocimientos matemáticos, así que permítanme hacer un ejemplo:

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

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

Nota: Si no le gusta este método, también puede tomar el factorial del número par antes del impar (ocho en este caso) y multiplicarlo por el número impar (es decir, 9! = 8! * 9).

Ahora implementémoslo en 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;
}

isFirstes una variable booleana declarada como estática; se utiliza para el primer caso en el que no queremos cambiar la suma anterior.

Pruebe tanto con números pares como impares.

Neeraj Jain
fuente
2

Puede utilizar la recursividad.

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

y luego, después de crear el método (función) anterior:

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

El único uso comercial para un factorial que se me ocurre son las fórmulas Erlang B y Erlang C, y no todo el mundo trabaja en un centro de llamadas o para la compañía telefónica. La utilidad de una función para las empresas parece dictar a menudo lo que aparece en un idioma: observe todas las funciones web, XML y de manejo de datos en los principales idiomas.

Es fácil mantener un fragmento de factorial o una función de biblioteca para algo como esto.

R Ubben
fuente
1

Un método muy sencillo para calcular factoriales:

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

He usado double porque pueden contener números masivos, pero puedes usar cualquier otro tipo como int, long, float, etc.

PD: Puede que esta no sea la mejor solución, pero soy nuevo en la codificación y me tomó mucho tiempo encontrar un código simple que pudiera calcular factoriales, así que tuve que escribir el método yo mismo, pero lo estoy poniendo aquí para que ayude a otras personas como yo. .

Kaamil Jasani
fuente
1

También puede usar la versión recursiva.

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

La recursividad suele ser menos eficiente debido a tener que empujar y hacer pop recursiones, por lo que la iteración es más rápida. Por otro lado, las versiones recursivas usan menos o ninguna variable local, lo cual es una ventaja.

Hesam
fuente
1

Factorial es una función discreta altamente creciente, así que creo que usar BigInteger es mejor que usar int. He implementado el siguiente código para el cálculo del factorial de enteros no negativos. He utilizado la recursividad en lugar de utilizar un bucle.

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

Aquí el rango del entero grande es

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

Sin embargo, el rango del método factorial dado anteriormente se puede extender hasta dos veces usando BigInteger sin firmar.

Sanjeet A
fuente
1

Tenemos una sola línea para calcularlo:

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

Un método bastante simple

    for ( int i = 1; i < n ; i++ )
    {
            answer = answer * i;
    }
DalekCaan99
fuente
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
fuente
0
public static int fact(int i){
    if(i==0)
       return 0;
    if(i>1){
       i = i * fact(--i);
    }

   return i;
}
Jaikrat
fuente
1
Creo que el OP está preguntando si hay una función en las API, no cómo escribir una. Además, 0! = 1 - es posible que desee actualizar su código para incluir ese caso.
SL Barth - Reincorpora a Monica el
resultará siempre 0
Tom Brito
0

Necesitamos implementar de forma iterativa. Si lo implementamos de forma recursiva, provocará StackOverflow si la entrada se vuelve muy grande (es decir, 2 mil millones). Y necesitamos usar un número de tamaño libre como BigInteger para evitar un desbordamiento aritmático cuando un número factorial se vuelve mayor que el número máximo de un tipo dado (es decir, 2 mil millones para int). Puede usar int para un máximo de 14 de factorial y long para un máximo de 20 de factorial antes del desbordamiento.

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

Si no puede usar BigInteger, agregue una comprobación de errores.

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
fuente
0
public int factorial(int num) {
        if (num == 1) return 1;
        return num * factorial(num - 1);
}
Scott Zhu
fuente
Debe usar long o BigInteger;)
AxelH
0

while loop (para números pequeños)

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
fuente
0

Conseguí esto de EDX úselo! se llama recursividad

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

con recursividad:

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

con bucle while:

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

USAR LA PROGRAMACIÓN DINÁMICA ES EFICIENTE

si desea usarlo para calcular una y otra vez (como el almacenamiento en caché)

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
fuente
0

usar la recursividad es el método más simple. si queremos encontrar el factorial de N, tenemos que considerar los dos casos donde N = 1 y N> 1 ya que en factorial seguimos multiplicando N, N-1, N-2 ,,,,, hasta 1. si vaya a N = 0 obtendremos 0 para la respuesta. Para evitar que el factorial llegue a cero, se utiliza el siguiente método recursivo. Dentro de la función factorial, mientras N> 1, el valor de retorno se multiplica por otro inicio de la función factorial. esto mantendrá el código llamando de forma recursiva al factorial () hasta que alcance N = 1 para el caso de N = 1, devuelve N (= 1) en sí mismo y todo el resultado previamente acumulado del retorno multiplicado N s se multiplica por N = 1. Así da el resultado factorial.

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