Чи існує метод, який обчислює факторіал на Java?


105

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

Відповіді:


26

Я не думаю, що було б корисно функціонувати бібліотеку для факторіалів. Існує велика кількість досліджень ефективних факторних реалізацій. Ось кілька реалізацій.


188
Чому не корисно функціонувати бібліотеку для фабрикантів?
midnite

17
Не багато людей насправді потребують фабрикантів у реальному коді. Якщо ви це робите, то, ймовірно, ви займаєтесь деякими розширеними математиками чи статистикою, і в цьому випадку ви, швидше за все, будете використовувати бібліотеку математики зі спеціалізованою факторною реалізацією.
mikera

3
Гамма-функція дуже корисна, тому вона включена до стандартної бібліотеки C ++.
Коламбо

2
Мені звучить, що @KarlthePagan означає, що не корисно мати стандартну функцію бібліотеки для факториалу - це правильно?
Дантістон

тож вони б попросили вас на співбесіді дізнатись, чи можете ви самі це зробити * мій випадок
moldovean

59

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


1
Так. Хороший матеріал. Існує реалізація факторіалу для плаваючих і не плоских чисел (MathUtils.factorial (int) і MathUtils.factorialDouble (int)), а також корисний природний логарифм n! (MathUtils.factorialLog (int))
Томаш Блахович

3
на даний момент це в ArithmeticUtils.
MarianP

6
ArithmeticUtils.factorial зараз як і раніше застарілий, атм використовують CombinatoricsUtils.factorial
Віктор Хяггвіст

Будь ласка, не використовуйте посилання! Вони, як правило, зникають (як ВСІ з них).
SMBiggs

1
@ScottBiggs Обидва посилання у відповіді працюють нормально.
Білл Ящірка

40
public class UsefulMethods {
    public static long factorial(int number) {
        long result = 1;

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

        return result;
    }
}

Версія великих номерів від 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); for (int i = 1; i <= n.intValue (); i ++) {factorial = factorial.multiply (BigInteger.valueOf (i)); } повернути факториал; }
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

Використовуйте BigIntegerMathнаступне:

BigInteger factorial = BigIntegerMath.factorial(n);

(Подібна функціональність для intтаlong доступна відповідно IntMathі LongMathвідповідно.)


6

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


6
Я згоден з вами, є більш важливі математичні функції. Але в моїх очах цей метод повинен бути стандартним, щоб люди могли його повторно використовувати. Немає необхідності впроваджувати це кілька разів кількома людьми. Для освітніх цілей вони можуть робити. Але на щоденну роботу вона застаріла. Це моя думка. У будь-якому випадку, дякую за відповіді. Я зроблю це самостійно - іншим разом.

Яка перевага запропонованої стандартизації? Додавання методів до стандартної бібліотеки не обійдеться без витрат. Як зазначали інші, не існує жодного найкращого рішення. Який із них ви пропонуєте побудувати на мові? Наявність методу в стандартній бібліотеці не допоможе вам заощадити час на розуміння вашої проблеми, і як тільки ви це зробите, ви можете також вибрати те, що найкраще підходить для роботи.
Метт

2
"... і всі знають, як написати факторну
James P.

4
Не погоджуюсь. Для комбінаторики необхідні фактори , які необхідні в багатьох областях розробки програмного забезпечення. Аргумент, що не включає факторіалів у вбудовану математичну бібліотеку, є тим самим аргументом, як і відсутність вбудованої математики.
Латеральний фрактал

Яка видатна логіка. Абсолютно зоряний. Шкода, що дизайнери класу 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!та правильність - максимум .


1
Дуже приємна ідея. враховуючи, що перших 20 факторіальностей, мабуть, достатньо, я б додав статичні константи (не потрібно обчислювати це кожного разу при запуску програми) до класу Math з тими наданими даними. Факт, що не багато людей потребує фабрикантів у своєму коді, є поганим приводом, щоб не підтримувати його в класі Math.
ТГ

6

Оскільки факториал росте так швидко, переповнення стека не є проблемою, якщо ви використовуєте рекурсію. Насправді значення 20! є найбільшим, який може представляти на Java довго. Таким чином, наступний метод буде або обчислити факторіальну (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

Коротка відповідь: використовувати рекурсію.

Ви можете створити один метод і викликати цей метод прямо в тому ж методі рекурсивно:

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));ви повинні отримати досить довго stactrace :)
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-loop є помилка: так і має бути 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

Просто не так :)

Тепер для непарних чисел: Якщо число непарне, додавання буде таким самим, як і у вас віднімають два щоразу, але ви зупиняєтесь на трьох. Однак кількість факторів змінюється. Якщо ви розділите число на два, ви отримаєте деяке число, що закінчується .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булева змінна, оголошена статичною; він використовується для 1-го випадку, коли ми не хочемо змінювати попередню суму.

Спробуйте як з парними, так і з непарними номерами.


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

Я використовував подвійний, оскільки вони можуть містити великі числа, але ви можете використовувати будь-який інший тип, наприклад, int, long, float тощо.

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


1

Ви також можете використовувати рекурсійну версію.

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

Рекурсія зазвичай менш ефективна через необхідність натискання та повторної рекурсії, тому ітерація швидша. З іншого боку, рекурсивні версії використовують менше або відсутність локальних змінних, що є перевагою.


1

Factorial дуже сильно збільшує дискретні функції. Тому я думаю, що використовувати 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);


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

Нам потрібно ітеративно реалізовувати. Якщо ми реалізуємо рекурсивно, це спричинить 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

цикл (для невеликих чисел)

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, повернене значення множиться з іншим ініціатором факторної функції. це дозволить коду рекурсивно викликати факториал () до тих пір, поки він не досягне 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;
    }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.