web-dev-qa-db-ja.com

Javaで再帰を使用した階乗

私は学習していますJava Java:The Complete Referenceを使用しています。現在、トピックRecursionに取り組んでいます。

注意: stackoverflowについても同様の質問があります。それらを検索しましたが、私の質問の解決策が見つかりませんでした。次のプログラムのロジックと混同しています。

以下のプログラムを実行すると、正しい出力が生成されますが、ロジックが理解できませんでした。

  • 次の行のロジックは理解できませんでした:result = fact(n-1)* n;
  • 私の知る限り、次のプログラムに示すようにn = 4の値を渡すと、
  • 次に、3 * 4が結果に保存されます(12)。
  • 繰り返しますが、fact(n-1)が呼び出されます。その後、nは3になります。
  • 次に、2 * 3が結果に保存され、前の12が置き換えられます。
  • 私が立ち往生している/混乱している場所を理解したと思います。

  • ありがとうございました。

class Calculation
{
    int fact(int n)
    {
        int result;

       if(n==1)
         return 1;

       result = fact(n-1) * n;
       return result;
    }
}

public class Factorial
{
     public static void main(String args[])
     {
       Calculation obj_one = new Calculation();

       int a = obj_one.fact(4);
       System.out.println("The factorial of the number is : " + a);
     }
}
25
user907629

resultは、factメソッドのローカル変数です。そのため、ファクトメソッドが呼び出されるたびに、結果は前回のファクト呼び出しとは異なる変数に格納されます。

そのため、事実として3を引数として呼び出すと、その結果は次のようになります。

 result3 = fact(2) * 3
 result3 = result2 * 3
 result3 = 1 * 2 * 3
10
JB Nizet

まず、階乗の仕組みを理解する必要があります。

4つましょう。例として。

_4! = 4 * 3 * 2 * 1 = 24
_

上記の例を使用してコードをシミュレートします。

_int fact(int n)
    {
        int result;
       if(n==0 || n==1)
         return 1;

       result = fact(n-1) * n;
       return result;
    }
_

ほとんどのプログラミング言語には、_function stack_と呼ばれるものがあります。それはちょうどカードのデッキのようなもので、各カードは他のカードの上に配置されます。そして、各カードは関数と考えることができるので、メソッドfactを渡します

スタックレベル1:fact(4) // n = 4 and is not equal to 1. So we call fact(n-1)*n

スタックレベル2:fact(3)

スタックレベル3:fact(2)

スタックレベル4:fact(1) //現在、n =1。したがって、この関数から1を返します。

値を返しています...

スタックレベル3:2 * fact(1) = 2 * 1 = 2

スタックレベル2:3 * fact(2) = 3 * 2 = 6

スタックレベル1:4 * fact(3) = 4 * 6 = 24

だから24。

次の行に注意してください。

_result = fact(n-1) * n;
           return result;
_

または単に:

_return fact(n-1) * n;
_

これにより、関数自体が呼び出されます。例として4を使用すると、

関数スタックに従って順番に..

_return fact(3) * 4;
return fact(2) * 3 * 4
return fact(1) * 2 * 3 * 4
_

結果を置換しています...

_return 1 * 2 * 3 * 4 = return 24
_

あなたがポイントを得ることを願っています。

50
Neigyl R. Noval

再帰を使用した階乗計算がどのように機能するかについて、さらに別の説明があります。

ソースコードを少し変更してみましょう。

int factorial(int n) {
      if (n <= 1)
            return 1;
      else
            return n * factorial(n - 1);
}

の計算の詳細は次のとおりです。

enter image description here

ソース: RECURSION(Java、C++)|アルゴリズムとデータ構造

19
Eugene Matiyuk

混乱は、result変数は1つしかないと考えているのに、実際には各関数呼び出しにresult変数が存在するからだと思います。そのため、古い結果は置き換えられず、返されます。

練り上げるには:

_int fact(int n)
{
    int result;

   if(n==1)
     return 1;

   result = fact(n-1) * n;
   return result;
}
_

fact(2)への呼び出しを想定します:

_int result;
if ( n == 1 ) // false, go to next statement
result = fact(1) * 2; // calls fact(1):
|    
|fact(1)
|    int result;  //different variable
|    if ( n == 1 )  // true
|        return 1;  // this will return 1, i.e. call to fact(1) is 1
result = 1 * 2; // because fact(1) = 1
return 2;
_

今より明確になってほしい。

9
Luchian Grigore
public class Factorial {

    public static void main(String[] args) {
        System.out.println(factorial(4));
    }

    private static long factorial(int i) {

        if(i<0)  throw new IllegalArgumentException("x must be >= 0"); 
        return i==0||i==1? 1:i*factorial(i-1);
    }
}
6
SanA

何が起こるかというと、再帰呼び出し自体がさらに再帰的な動作になります。あなたがそれを書き出すなら、あなたは得る:

 fact(4)
 fact(3) * 4;
 (fact(2) * 3) * 4;
 ((fact(1) * 2) * 3) * 4;
 ((1 * 2) * 3) * 4;
5
rsp

ここで欠けている重要な点は、変数「結果」がスタック変数であり、そのため「置換」されないことです。詳しく説明すると、ファクトが呼び出されるたびに、「result」という新しい変数がインタープリターの内部で作成され、メソッドの呼び出しにリンクされます。これは、特定のメソッド呼び出しではなく、オブジェクトのインスタンスにリンクしたオブジェクトフィールドとは対照的です

3
idanzalz

三項演算子を使用した再帰的なソリューション。

public static int fac(int n) {
    return (n < 1) ? 1 : n*fac(n-1);
}
1
martynas

これは古いものですが、グーグルでもかなりうまくいき続けています。だから私はこれに言及するだろうと思った。 x =の場合にチェックする人はいません。

0!そして1!両方= 1。

これは以前の回答では確認されていないため、fact(0)が実行された場合、スタックオーバーフローが発生します。とにかく簡単な修正:

public static int fact(int x){
    if (x==1 | x==0)
        return 1;
    return fact(x-1) * x;
}// fact
1
prasanthv

それを理解するには、可能な限り簡単な方法でメソッドを宣言する必要があり、5月6日の投稿でマーティナスがそれを打ち付けました。

int fact(int n) {
    if(n==0) return 1;
    else return n * fact(n-1);
}

上記の実装を読んで理解してください。

1
Eric Espino

Java 8以降、再帰自体を使用する

  UnaryOperator<Long> fact = num -> num<1 ? 1 : num * this.fact.apply(num-1);

そして、それを次のように使用します

  fact.apply(5); // prints 120

内部的には

5*(4*(3*(2*(1*(1)))))
0
Arundev

私の意見では、Javaの初心者レベルの知識を持つ人の意見であるため、n == 1をn <= 1または(n == 0)||(n == 1)に変更することをお勧めします0の階乗は1だからです。

0
user3255993

正しいものは次のとおりです。

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

これは階乗0に対して1を返します。私を信じてください。私はこれを難しい方法で学びました。 0の条件を維持しないために、インタビューをクリアできませんでした。

0
Bikram Kundu

私見、再帰関連のアクションを理解するためのキーは次のとおりです。

  1. 最初に、再帰的にスタックに飛び込み、呼び出しごとに、何らかの方法で値を変更します(例:n-1 in func(n-1);)。
  2. RecursionStopConditionが満たされると(例:n == 0)、再帰が停止し、メソッドは実際の作業を行い、値を上位スタックの呼び出し元メソッドに返し、スタックの最上部にバブリングします。
  3. より深いスタックから返された値をキャッチし、何らかの方法で変更し(この場合はnを掛ける)、この変更された値をスタックの上位に返すことが重要です。よくある間違いは、最も深いスタックフレームの値がスタックの最上部に直接返されるため、すべてのメソッド呼び出しが無視されることです。

確かに、メソッドは再帰に飛び込む前(スタックの最上部から最下部)またはその途中で有用な作業を行うことができます。

0
Konstantin