Javaで階乗を計算するメソッドはありますか?


105

まだ見つかりませんでした。私は何か見落としてますか?階乗法が初心者にとって一般的なサンプルプログラムであることは知っています。しかし、これを再利用するための標準的な実装があると便利ではないでしょうか?私はそのようなメソッドを標準型(int、long ...など)とBigInteger / BigDecimalでも使用できます。

回答:


26

階乗用のライブラリー関数があると便利だとは思いません。効率的な階乗の実装については、かなりの研究があります。ここにいくつかの実装があります。


188
階乗のライブラリー関数があると便利ではないのはなぜですか?
midnite 2013

17
実際のコードで階乗を実際に必要とする人は多くありません。もしそうなら、あなたはおそらくいくつかの高度な数学または統計を行っています、その場合、おそらくあなたはおそらく特殊化した階乗の実装で数学ライブラリを使用しているでしょう。
mikera 2013

3
ガンマ関数は非常に便利です。そのため、C ++標準ライブラリに含まれています。
コロンボ2016

2
@KarlthePaganは階乗の標準ライブラリ関数を使用することは役に立たないことを意味すると私には思えます-それは正しいですか?
ダンティストン2017年

そのため、インタビューで、あなたが自分でできるかどうかを尋ねます。*私の場合
モルドバ語

59

Apache Commons Mathには、MathUtilsクラスにいくつかの階乗メソッドがあります。


1
うん。いい物。floatおよびnon-flatの階乗の実装(MathUtils.factorial(int)およびMathUtils.factorialDouble(int))と、nの便利な自然対数もあります。(MathUtils.factorialLog(int))
TomaszBłachowicz2009年

3
現時点ではArithmeticUtilsにあります。
MarianP 2013年

6
ArithmeticUtils.factorialは現在廃止されているようです。atmuse CombinatoricsUtils.factorial
VictorHäggqvist2014年

リンクは使用しないでください。彼らは(これらすべてのように)消える傾向があります。
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による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、2016

2
Big Numbersバージョン: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 2016

23

裸の裸の階乗が実際に必要になることはほとんどありません。ほとんどの場合、次のいずれかが必要になります。

1)ある階乗を別の階乗で割る、または

2)近似浮動小数点解。

どちらの場合も、シンプルなカスタムソリューションの方が適しています。

(1)の場合、x = 90の場合、/ 85!、90を保持する必要なく、x = 86 * 87 * 88 * 89 * 90と同じように結果を計算します。メモリ内:)

ケース(2)では、「スターリングの近似」をグーグル検索してください。


3
反例:N個の要素を含む順列の数を計算するには、階乗が必要であり、順列を保持する構造を割り当てる場合に必要です。
マークジェロニムス2016年

いい視点ね!私の最初の質問は、90!/ 85!5の共通分母のために簡略化されていますが、実際には、それは85の共通分母でした。90!/ 85!= 90 * 89 * 88 * 87 * 86 * 85!/ 85!。あなたはその平等でそれをよりはっきりと見ることができます。
HoldOffHunger 2016

12

BigIntegerMath次のようにGuavaを使用します。

BigInteger factorial = BigIntegerMath.factorial(n);

(同様の機能のためにintlongで利用可能であるIntMathLongMathそれぞれ)。


6

階乗は初心者のプログラマにとってはいい練習になりますが、ほとんどの場合あまり役に立ちませんし、階乗関数の書き方を誰もが知っているため、通常、平均的なライブラリにはありません。


6
私はあなたに同意します、もっと重要な数学関数があります。しかし、私の目には、この方法は標準であるはずなので、人々はそれを再利用できます。複数の人が何度も実装する必要はありません。教育目的のために、これを行う場合があります。しかし、毎日の仕事にとってそれは時代遅れです。これは私の意見。とにかく、答えてくれてありがとう。私は自分でやります-別の機会に。

提案された標準化の利点は何ですか?メソッドを標準ライブラリに追加するには、コストがかかります。他の人が指摘したように、単一の最良の解決策はありません。どちらを言語に組み込むことを提案しますか?標準ライブラリにメソッドがあることは、問題を理解する時間を節約することにはなりません。それが完了したら、その仕事に最適な実装を選択することもできます。
マットG

2
「...そして階乗関数の書き方は誰もが知っている」chaosinmotion.com/blog/?p=622
James P.

4
同意しない。階乗はCombinatoricsに必要です。これは、ソフトウェア設計の多くの領域で必要です。組み込みの数学ライブラリに階乗を含まない引数は、組み込みの数学ライブラリがない場合と同じ引数です。
LateralFractal 2013

なんて傑出したロジックだろう。絶対に恒星。java.lang.Mathクラスの設計者が、abs()メソッドをそのlibに組み込んだとき、それを知らなかったのは残念です。
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で表現できる最大のものです。したがって、次のメソッドは、nが大きすぎる場合、factorial(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のストリームを使用したFactorialsの詳細を読む



6

短い答えは、再帰を使用することです。

1つのメソッドを作成し、そのメソッドを同じメソッド内で再帰的に呼び出すことができます。

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++)
クリス

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

シンプルじゃないですか:)

Now For Odd Numbers:数が奇数の場合、加算は同じです。毎回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、およびWeb関数を確認してください。

このようなもののために階乗スニペットやライブラリ関数を保持するのは簡単です。


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を使用して最大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に関数を記述する方法ではなく、APIに関数があるかどうかを尋ねていると思います。また、0!= 1-そのケースを含むようにコードを更新したい場合があります。
SLバース-モニカ

常に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

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の2つのケースを考慮する必要があります。階乗では、N、N-1、N-2 ,,,,を1まで乗算し続けるためです。 N = 0に行くと、答えは0になります。階乗がゼロに達するのを防ぐために、次の再帰的方法が使用されます。階乗関数内では、N> 1の間、戻り値は階乗関数の別の開始と乗算されます。これは、N = 1に到達するまで、コードがfactorial()を再帰的に呼び出し続けます。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;
    }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.