Есть ли в Java метод вычисления факториала?

105

Я пока не нашел. Я что-то пропустил? Я знаю, что факторный метод - это распространенный пример программы для начинающих. Но разве не было бы полезно иметь для этого стандартную реализацию для повторного использования? Я мог бы использовать такой метод со стандартными типами (например, int, long ...), а также с BigInteger / BigDecimal.

Леоморд
источник

Ответы:

26

Не думаю, что было бы полезно иметь библиотечную функцию для factorial. Существует много исследований эффективных факториальных реализаций. Вот несколько реализаций.

Карл язычник
источник
189
Почему не полезно иметь библиотечную функцию для факториала?
midnite
17
Не многим людям действительно нужны факториалы в реальном коде. Если да, то вы, вероятно, занимаетесь некоторыми сложными математическими вычислениями или статистикой, и в этом случае вы, скорее всего, уже используете математическую библиотеку со специализированной факториальной реализацией.
mikera
3
Гамма-функция очень полезна, поэтому она включена в стандартную библиотеку C ++.
Коломбо
2
Мне кажется, что @KarlthePagan означает бесполезность стандартной библиотечной функции для факториала - это правильно?
dantiston
чтобы они спросили вас на собеседовании, сможете ли вы сделать это самостоятельно * мой случай
moldovean
59

В Apache Commons Math есть несколько факториальных методов в классе MathUtils .

Билл Ящерица
источник
1
Ага. Хорошая вещь. Существует реализация факториала для чисел с плавающей запятой и неплоских чисел (MathUtils.factorial (int) и MathUtils.factorialDouble (int)), а также полезный натуральный логарифм n! (MathUtils.factorialLog (int))
Томаш Блахович
3
он сейчас находится в ArithmeticUtils.
MarianP
6
ArithmeticUtils.factorial теперь, по-
видимому, устарел, банкомат
Пожалуйста, не используйте ссылки! Они имеют тенденцию исчезать (как и ВСЕ они).
SMBiggs
1
@ScottBiggs Обе ссылки в ответе работают нормально.
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;
    }
}

Версия Big Numbers от 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;
}
сародж адхикари
источник
Также вы предполагаете, что вам нужен факториал целого числа
Эндрю
Это решение должно использовать класс BigInteger.
Олег Абражаев
2
Версия больших чисел: общедоступный статический факториал BigInteger (BigInteger n) {BigInteger factorial = BigInteger.valueOf (1); для (int я = 1; я <= n.intValue (); я ++) {factorial = factorial.multiply (BigInteger.valueOf (я)); } вернуть факториал; }
HoldOffHunger
23

На практике голые факториалы нужны редко. Чаще всего вам понадобится одно из следующего:

1) разделить один факториал на другой, или

2) приближенный ответ с плавающей запятой.

В обоих случаях вам лучше использовать простые индивидуальные решения.

В случае (1), скажем, если x = 90! / 85 !, то вы рассчитаете результат как x = 86 * 87 * 88 * 89 * 90, без необходимости удерживать 90! в памяти :)

В случае (2) введите в Google "приближение Стирлинга".

Игорь Кривоконь
источник
3
Контрпример: для вычисления числа перестановок с N элементами требуется чистый факториал, и он необходим, если вы хотите выделить структуру для хранения перестановок.
Марк Иеронимус
Хорошая точка зрения! Мой первый вопрос был ли 90! / 85! упрощено из-за общего знаменателя 5, но на самом деле это был общий знаменатель 85 !. 90! / 85! = 90 * 89 * 88 * 87 * 86 * 85! / 85 !. Более ясно это видно в этом равенстве.
HoldOffHunger
12

Используйте Guava BigIntegerMathследующим образом:

BigInteger factorial = BigIntegerMath.factorial(n);

(Аналогичные функции для intи longдоступны в IntMathи LongMathсоответственно.)

кендырь
источник
6

Хотя факториалы - хорошее упражнение для начинающего программиста, в большинстве случаев они не очень полезны , и каждый знает, как написать факториальную функцию, поэтому обычно их нет в средней библиотеке.

бдонлан
источник
6
Я согласен с вами, есть более важные математические функции. Но на мой взгляд, этот метод должен быть стандартным, чтобы люди могли его повторно использовать. Нет необходимости внедрять его несколько раз несколькими людьми. Для образовательных целей это можно сделать. Но для повседневной работы он устарел. Это мое мнение. В любом случае, спасибо за ответ. Я сделаю это сам - в другой раз.
В чем преимущество предлагаемой вами стандартизации? Добавление методов в стандартную библиотеку не обходится без затрат. Как отмечали другие, не существует единственного лучшего решения. Какой из них вы предлагаете встроить в язык? Наличие метода в стандартной библиотеке не сэкономит вам времени на понимание вашей проблемы, и как только вы это сделаете, вы можете также выбрать реализацию, которая лучше всего подходит для работы.
Matt G
2
«... и все знают, как написать факториальную функцию» chaosinmotion.com/blog/?p=622
Джеймс П.
4
Не согласен. Факториалы необходимы для комбинаторики , которая необходима во многих областях разработки программного обеспечения. Аргумент не включать факториалы во встроенную математическую библиотеку - это тот же аргумент, что и отсутствие встроенной математической библиотеки.
LateralFractal
Какая выдающаяся логика. Абсолютно звездный. Жаль, что разработчики класса java.lang.Math не заметили этого, когда включили методы abs () в эту библиотеку.
Игорь Судакевич
6

Я считаю, что это был бы самый быстрый способ с помощью таблицы поиска:

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

Для собственного типа long(8 байт) он может содержать не более20!

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

Очевидно, что 21!вызовет переполнение.

Следовательно, для собственного типа разрешено, имеет смысл и правильный longмаксимум 20!.

Midnite
источник
1
Неплохая идея. учитывая, что этого факториала первых 20, вероятно, достаточно, я бы добавил статические константы (нет необходимости вычислять их каждый раз при запуске приложения) в класс Math с этими предоставленными данными. Тот факт, что не многим людям нужны факториалы в своем коде, - плохой предлог, чтобы не поддерживать их в классе Math.
TG
6

Поскольку факториал растет так быстро, переполнение стека не является проблемой, если вы используете рекурсию. На самом деле значение 20! является самым большим из возможных в Java long. Таким образом, следующий метод либо вычислит factorial (n), либо выдаст исключение IllegalArgumentException, если n слишком велико.

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

Другой (более крутой) способ сделать то же самое - использовать потоковую библиотеку Java 8 следующим образом:

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

Подробнее о факториалах с использованием потоков Java 8

Пер-Оке Минборг
источник
6

В пакете Apache Commons Math есть факторный метод , я думаю, вы могли бы его использовать.

Валентин Роше
источник
Обновленная ссылка это
Marco Lackovic
@Krige только что исправил ссылку
fedorqui 'ТАК, хватит вредить'
6

Короткий ответ: используйте рекурсию.

Вы можете создать один метод и рекурсивно вызвать его прямо внутри того же метода:

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
рекурсивно функции хороши, но если кто-то попробует посчитать действительно большой фактоориал, у них будет StackOverflowException;) + Я не уверен, но я думаю, что рекурсия медленнее, чем старый добрый метод цикла;)
TG
как вы можете узнать, что это закончится исключением stackoverflow? @TG
gumuruh
2
Это просто. каждый рекурсивно помещает текущее место в стек, поэтому у программы будет «память» места для возврата после завершения вызова метода. У стека есть свои пределы. чтобы попробовать это на себе, попробуйте изменить код выше, System.out.println(calc(10));чтобы System.out.println(calc(Long.MAX_VALUE));получить довольно длинную трассу :)
TG
@TG Для ясности я попробовал свой рекурсивный метод, с которым работает BigInteger. Я попытался вычислить факториал числа, 8020которое дало мне результат 613578884952214809325384...с 27831десятичными знаками. Так что даже при работе с числами это огромное «нет» Stackoverflowбудет выброшено. Конечно, вы правы, но я сомневаюсь, что есть такие большие числа, которые можно использовать на практике :-)
Мориц Шмидт
3

Попробуй это

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
Я считаю, что в цикле for есть ошибка: так и должно быть i <= value. Цикл for можно немного оптимизировать до (int i = 2; i <= value; i++).
Крис
2

Я нашел удивительный трюк, чтобы найти факториалы всего лишь за половину действительного умножения.

Пожалуйста, проявите терпение, так как это довольно длинный пост.

Для четных чисел: чтобы уменьшить умножение на четные числа вдвое, вы получите n / 2 множителя. Первым фактором будет число, от которого вы берете факториал, затем следующим будет это число плюс это число минус два. Следующим числом будет предыдущее число плюс последнее добавленное число минус два. Вы закончили, когда последним добавленным числом было два (т.е. 2) . Вероятно, в этом не было особого смысла, поэтому позвольте мне привести вам пример.

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

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

Обратите внимание, что я начал с 8, затем первое число, которое я добавил, было 6, затем 4, затем 2, каждое добавленное число было на два меньше, чем число, добавленное перед ним. Этот метод эквивалентен умножению наименьших чисел на наибольшие числа, только с меньшим умножением, например:

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

Все просто :)

Теперь для нечетных чисел: если число нечетное, сложение такое же, как если бы вы каждый раз вычитали два, но вы останавливаетесь на трех. Однако количество факторов меняется. Если разделить число на два, получится число, оканчивающееся на 0,5. Причина в том, что если мы умножим концы вместе, у нас останется среднее число. По сути, все это может быть решено путем решения ряда факторов, равных числу, деленному на два, с округлением в большую сторону. Вероятно, это тоже не имело большого смысла для умов без математического образования, поэтому позвольте мне привести пример:

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

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

Примечание. Если вам не нравится этот метод, вы также можете просто взять факториал четного числа перед нечетным (в данном случае восемь) и умножить его на нечетное число (т.е. 9! = 8! * 9).

Теперь реализуем это на 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это логическая переменная, объявленная статической; он используется для первого случая, когда мы не хотим изменять предыдущую сумму.

Попробуйте как с четными, так и с нечетными числами.

Нирадж Джайн
источник
2

Вы можете использовать рекурсию.

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

а затем после создания метода (функции) выше:

System.out.println(factorial(number of your choice));  
    //direct example
    System.out.println(factorial(3));
Кристиан Бабаруси
источник
1

Единственное коммерческое использование факториала, которое я могу придумать, - это формулы Erlang B и Erlang C, и не все работают в колл-центре или в телефонной компании. Кажется, что полезность функции для бизнеса часто диктует то, что отображается на языке - посмотрите на всю обработку данных, XML и веб-функции на основных языках.

Для чего-то вроде этого легко сохранить факториальный сниппет или библиотечную функцию.

Р Уббен
источник
1

Очень простой метод вычисления факториалов:

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

Я использовал double, потому что они могут содержать большие числа, но вы можете использовать любой другой тип, например int, long, float и т. Д.

PS Это может быть не лучшее решение, но я новичок в кодировании, и мне потребовалось много времени, чтобы найти простой код, который мог бы вычислять факториалы, поэтому мне пришлось написать метод сам, но я помещаю его здесь, чтобы он помог другим людям, таким как я .

Камил Джасани
источник
1

Вы также можете использовать версию с рекурсией.

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

Рекурсия обычно менее эффективна из-за необходимости подталкивать и выталкивать рекурсии, поэтому итерация выполняется быстрее. С другой стороны, рекурсивные версии используют меньше локальных переменных или вообще не используют их, что является преимуществом.

Хесам
источник
1

Факториал - это сильно увеличивающаяся дискретная функция, поэтому я думаю, что использование BigInteger лучше, чем использование int. Я реализовал следующий код для вычисления факториала неотрицательных целых чисел. Я использовал рекурсию вместо использования цикла.

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

Здесь диапазон больших целых чисел

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

Однако диапазон факториального метода, приведенного выше, можно расширить до двух раз с помощью беззнакового BigInteger.

Санджит А
источник
1

У нас есть одна строка для его вычисления:

Long factorialNumber = LongStream.rangeClosed(2, N).reduce(1, Math::multiplyExact);
krmanish007
источник
1

Достаточно простой способ

    for ( int i = 1; i < n ; i++ )
    {
            answer = answer * i;
    }
DalekCaan99
источник
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
источник
0
public static int fact(int i){
    if(i==0)
       return 0;
    if(i>1){
       i = i * fact(--i);
    }

   return i;
}
Джайкрат
источник
1
Я думаю, что OP спрашивает, есть ли функция в API, а не как ее написать. Кроме того, 0! = 1 - вы можете обновить свой код, чтобы включить этот регистр.
SL Barth - Reinstate Monica
всегда будет 0
Tom Brito
0

Нам нужно реализовывать итеративно. Если мы реализуем рекурсивно, это вызовет StackOverflow, если ввод станет очень большим (т.е. 2 миллиарда). И нам нужно использовать несвязанное число размера, такое как BigInteger, чтобы избежать арифматического переполнения, когда факториальное число становится больше, чем максимальное число данного типа (т.е. 2 миллиарда для int). Вы можете использовать int для максимум 14 факториалов и long для максимум 20 факториалов до переполнения.

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

Если вы не можете использовать BigInteger, добавьте проверку ошибок.

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);
}
Скотт Чжу
источник
Следует использовать long или BigInteger;)
AxelH
0

цикл while (для небольших чисел)

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

Получил от EDX, пользуйся! это называется рекурсией

   public static int factorial(int n) {
    if (n == 1) {
        return 1;
    } else {
        return n * factorial(n-1);
    }
}
Рик
источник
0

с рекурсией:

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

с циклом while:

public static int factorial1(int n)
{
    int fact=1;
    while(n>=1)
    {
        fact=fact*n;
        n--;
    }
    return fact;
}
Нитеш Гупта
источник
0

ИСПОЛЬЗОВАНИЕ ДИНАМИЧЕСКОГО ПРОГРАММИРОВАНИЯ ЭФФЕКТИВНО

если вы хотите использовать его для вычисления снова и снова (например, кеширование)

Код 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

использование рекурсии - самый простой метод. если мы хотим найти факториал N, мы должны рассмотреть два случая, когда N = 1 и N> 1, поскольку в факториале мы продолжаем умножать N, N-1, N-2 ,,,,, до 1. если мы перейти к N = 0 мы получим 0 за ответ. чтобы помешать факториалу достичь нуля, используется следующий рекурсивный метод. Внутри функции факториала, пока N> 1, возвращаемое значение умножается на другое инициирование функции факториала. это будет держать код, рекурсивно вызывающий factorial (), пока он не достигнет N = 1. для случая N = 1 он сам вернет N (= 1), и весь ранее созданный результат умноженного возврата N s умножается на N = 1. Таким образом дает факториальный результат.

static int factorial(int N) {
    if(N > 1) { 
    return n * factorial(N - 1);
    }
    // Base Case N = 1
    else { 
    return N;
    }
Вишака Баснаяке
источник