Java에서 계승을 계산하는 방법이 있습니까?


105

아직 찾지 못했습니다. 내가 뭐 놓친 거 없니? 팩토리얼 방법이 초보자를위한 일반적인 예제 프로그램이라는 것을 알고 있습니다. 그러나 이것을 재사용하기 위해 표준 구현을 갖는 것이 유용하지 않을까요? 표준 유형 (예 : int, long ...) 및 BigInteger / BigDecimal에서도 이러한 방법을 사용할 수 있습니다.

답변:


26

계승에 대한 라이브러리 기능을 갖는 것이 유용하지 않을 것이라고 생각합니다. 효율적인 팩토리얼 구현에 대한 많은 연구가 있습니다. 다음은 몇 가지 구현입니다.


188
팩토리얼에 대한 라이브러리 함수가 유용하지 않은 이유는 무엇입니까?
midnite

17
실제로 실제 코드에서 팩토리얼이 필요한 사람은 많지 않습니다. 그렇게한다면 아마도 고급 수학이나 통계를 수행하고있을 것입니다.이 경우 이미 특수 요인 구현이 포함 된 수학 라이브러리를 사용하고있을 것입니다.
mikera

3
감마 함수는 매우 유용하므로 C ++ 표준 라이브러리에 포함되어 있습니다.
Columbo

2
@KarlthePagan 이 팩토리얼에 대한 표준 라이브러리 함수를 사용 하는 것이 유용하지 않다는 의미로 들립니다. 맞습니까?
dantiston

당신이 * 그것을 내 경우 자신 할 수 있다면 그들은 확인하기 위해 인터뷰를 요청할 것 때문에
moldovean

59

Apache Commons Math 에는 MathUtils 클래스 에 몇 가지 팩토리얼 메서드가 있습니다.


1
네. 좋은 물건. float 및 non-flat 숫자 (MathUtils.factorial (int) 및 MathUtils.factorialDouble (int))에 대한 계승 구현이 있으며 n의 유용한 자연 로그도 있습니다! (
MathUtils.factorialLog

3
현재 ArithmeticUtils에 있습니다.
MarianP 2013-10-16

6
ArithmeticUtils.factorial가 appearently 지금은 사용되지 않습니다, ATM 사용 CombinatoricsUtils.factorial
빅터 Häggqvist

링크를 사용하지 마십시오! 그들은 사라지는 경향이 있습니다 (이것들 모두가 그렇듯이).
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;
    }
}

HoldOffHunger의 Big Numbers 버전 :

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

또한 정수의 계승을 원한다고 가정합니다
Andrew

이 솔루션은 BigInteger 클래스를 사용해야합니다.
Oleg Abrazhaev

2
큰 숫자 버전 : public static BigInteger factorial (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 !, 그러면 90을 유지할 필요없이 x = 86 * 87 * 88 * 89 * 90과 같이 결과를 계산합니다! 메모리에 :)

(2)의 경우 "Stirling의 근사치"에 대해 google.


3
반례 : N 개 요소로 순열 수를 계산하려면 베어 계승이 필요하며 순열을 보유 할 구조를 할당하려는 경우 필요합니다.
Mark Jeronimus

좋은 지적! 내 첫 번째 질문은 90! / 85! 5의 공통 분모 때문에 단순화되었지만 실제로는 85!의 공통 분모였습니다. 90! / 85! = 90 * 89 * 88 * 87 * 86 * 85! / 85 !. 그 평등에서 더 명확하게 볼 수 있습니다.
HoldOffHunger

12

BigIntegerMath다음과 같이 Guava를 사용하십시오 .

BigInteger factorial = BigIntegerMath.factorial(n);

( intlong에 대한 유사한 기능 은 IntMathLongMath각각 에서 사용할 수 있습니다 .)


6

팩토리얼은 초보 프로그래머에게 좋은 연습을 제공하지만 대부분의 경우 유용 하지 않으며 모든 사람이 팩토리얼 함수를 작성하는 방법을 알고 있으므로 일반적으로 평균 라이브러리에 없습니다.


6
동의합니다. 더 중요한 수학적 함수가 있습니다. 하지만 내 눈에이 방법은 사람들이 재사용 할 수 있도록 표준이어야합니다. 여러 사람이 여러 번 구현할 필요가 없습니다. 교육 목적으로 할 수 있습니다. 그러나 매일 일하는 것은 쓸모가 없습니다. 제 의견입니다. 어쨌든 답변 해주셔서 감사합니다. 나는 혼자서 할 것입니다.

제안 된 표준화의 이점은 무엇입니까? 표준 라이브러리에 메소드를 추가하는 데 비용이 들지 않습니다. 다른 사람들이 지적했듯이 최상의 솔루션 은 하나도 없습니다 . 어떤 언어로 구축 할 것을 제안합니까? 표준 라이브러리에 메소드가 있다고해서 문제를 이해하는 시간이 절약되지는 않으며, 일단 작업을 완료하면 작업에 가장 적합한 구현을 선택하는 것이 좋습니다.
Matt G

2
"... 그리고 모두가 계승 함수를 작성하는 방법을 알고 있습니다." chaosinmotion.com/blog/?p=622
James P.

4
동의하지 않는다. 팩토리얼은 소프트웨어 설계의 많은 영역에서 필요한 조합 학에 필요합니다 . 내장 수학 라이브러리에 계승을 포함하지 않는 인수는 내장 수학 라이브러리가없는 것과 동일한 인수입니다.
LateralFractal 2013 년

정말 뛰어난 논리입니다. 절대적으로 별. java.lang.Math 클래스 디자이너가 해당 lib에 abs () 메서드를 포함 할 때이를 무지했습니다.
Igor Soudakevitch

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 클래스에서 팩토리얼을 지원하지 않는 것은 좋지 않습니다.
TG

6

팩토리얼이 너무 빨리 증가하기 때문에 재귀를 사용하는 경우 스택 오버플로는 문제가되지 않습니다. 사실, 가치 20! Java long에서 나타낼 수있는 가장 큰 것입니다. 따라서 다음 메서드는 factorial (n)을 계산하거나 n이 너무 크면 IllegalArgumentException을 발생시킵니다.

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던져 지지 않을 것입니다. 당연히 맞아요.하지만 실제 사용하면 그렇게 큰 숫자가 있는지 의심 스럽습니다. :-)
Moritz Schmidt

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++).
Chris

2

실제 곱셈의 절반에 불과한 계승을 찾는 놀라운 트릭을 찾았습니다.

이것은 약간 긴 게시물이므로 잠시 기다려주십시오.

짝수 : 짝수로 곱셈을 반으로 줄이려면 n / 2 인수로 끝납니다. 첫 번째 요소는 계승을 취하는 숫자이고 다음 요소는 해당 숫자에 2를 더한 숫자입니다. 다음 숫자는 이전 숫자에 마지막으로 추가 된 숫자에서 2를 뺀 숫자입니다. 마지막으로 추가 한 숫자가 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였으며 각 숫자는 이전에 추가 된 숫자보다 2가 적습니다. 이 방법은 다음과 같이 더 적은 곱셈으로 가장 작은 숫자와 가장 큰 숫자를 곱하는 것과 같습니다.

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

간단하지 않습니까 :)

이제 홀수 : 숫자가 홀수이면 매번 2를 빼는 것과 같이 더하기는 동일하지만 3에서 멈 춥니 다. 그러나 요인의 수는 변경됩니다. 숫자를 2로 나누면 .5로 끝나는 숫자가됩니다. 그 이유는 끝을 함께 곱하면 중간 숫자가 남기 때문입니다. 기본적으로이 모든 것은 2로 나눈 숫자와 같은 여러 요인을 반올림하여 풀 수 있습니다. 이것은 아마도 수학적 배경이없는 사람에게는별로 이해가되지 않았을 것이므로 예를 들어 보겠습니다.

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

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

참고 : 이 방법이 마음에 들지 않으면 홀수 앞의 짝수 (이 경우 8)의 계승을 취하고 홀수 (예 : 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 등과 같은 다른 유형을 사용할 수 있습니다.

추신 이것은 최선의 해결책이 아닐 수도 있지만 코딩에 익숙하지 않고 계승을 계산할 수있는 간단한 코드를 찾는 데 오랜 세월이 걸렸기 때문에 직접 방법을 작성해야했지만 여기에 넣어서 나와 같은 다른 사람들을 돕습니다. .


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.

그러나 위에 주어진 팩토리얼 방법의 범위는 unsigned BigInteger를 사용하여 최대 2 배까지 확장 될 수 있습니다.


1

이를 계산하는 한 줄이 있습니다.

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

1

상당히 간단한 방법

    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
OP가 API에 함수가 있는지 묻는 것이지 작성 방법이 아니라고 생각합니다. 또한 0! = 1-해당 사례를 포함하도록 코드를 업데이트 할 수 있습니다.
SL Barth-Monica 복원

결과는 항상 0
Tom Brito

0

우리는 반복적으로 구현해야합니다. 재귀 적으로 구현하면 입력이 매우 커지면 (예 : 20 억) StackOverflow가 발생합니다. 그리고 계승 수가 주어진 유형의 최대 수 (즉, int의 경우 20 억)보다 커질 때 산술적 오버플로를 방지하기 위해 BigInteger와 같은 바인딩되지 않은 크기 수를 사용해야합니다. 오버플로 전에 최대 14 개의 팩토리얼에 int를 사용하고 최대 20 개의 팩토리얼에 대해 long을 사용할 수 있습니다.

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

동적 프로그래밍을 사용하는 것이 효율적입니다.

그것을 사용하여 반복해서 계산하고 싶다면 (캐싱과 같이)

자바 코드 :

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이됩니다. 팩토리얼이 0에 도달하는 것을 막기 위해 다음과 같은 재귀 방법이 사용됩니다. 계승 함수 내에서 N> 1 인 동안 반환 값은 계승 함수의 다른 시작과 곱해집니다. 이것은 N = 1 케이스에 대해 N = 1에 도달 할 때까지 factorial ()을 재귀 적으로 호출하는 코드를 유지하고, N (= 1) 자체를 반환하고 곱해진 반환 N의 이전에 구축 된 모든 결과는 N으로 곱해집니다. = 1. 따라서 계승 결과를 제공합니다.

static int factorial(int N) {
    if(N > 1) { 
    return n * factorial(N - 1);
    }
    // Base Case N = 1
    else { 
    return N;
    }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.