私は学習していますJava Java:The Complete Referenceを使用しています。現在、トピックRecursionに取り組んでいます。
注意: stackoverflowについても同様の質問があります。それらを検索しましたが、私の質問の解決策が見つかりませんでした。次のプログラムのロジックと混同しています。
以下のプログラムを実行すると、正しい出力が生成されますが、ロジックが理解できませんでした。
私が立ち往生している/混乱している場所を理解したと思います。
ありがとうございました。
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); } }
result
は、fact
メソッドのローカル変数です。そのため、ファクトメソッドが呼び出されるたびに、結果は前回のファクト呼び出しとは異なる変数に格納されます。
そのため、事実として3を引数として呼び出すと、その結果は次のようになります。
result3 = fact(2) * 3
result3 = result2 * 3
result3 = 1 * 2 * 3
まず、階乗の仕組みを理解する必要があります。
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
_
あなたがポイントを得ることを願っています。
再帰を使用した階乗計算がどのように機能するかについて、さらに別の説明があります。
ソースコードを少し変更してみましょう。
int factorial(int n) {
if (n <= 1)
return 1;
else
return n * factorial(n - 1);
}
!の計算の詳細は次のとおりです。
混乱は、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;
_
今より明確になってほしい。
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);
}
}
何が起こるかというと、再帰呼び出し自体がさらに再帰的な動作になります。あなたがそれを書き出すなら、あなたは得る:
fact(4)
fact(3) * 4;
(fact(2) * 3) * 4;
((fact(1) * 2) * 3) * 4;
((1 * 2) * 3) * 4;
ここで欠けている重要な点は、変数「結果」がスタック変数であり、そのため「置換」されないことです。詳しく説明すると、ファクトが呼び出されるたびに、「result」という新しい変数がインタープリターの内部で作成され、メソッドの呼び出しにリンクされます。これは、特定のメソッド呼び出しではなく、オブジェクトのインスタンスにリンクしたオブジェクトフィールドとは対照的です
三項演算子を使用した再帰的なソリューション。
public static int fac(int n) {
return (n < 1) ? 1 : n*fac(n-1);
}
これは古いものですが、グーグルでもかなりうまくいき続けています。だから私はこれに言及するだろうと思った。 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
それを理解するには、可能な限り簡単な方法でメソッドを宣言する必要があり、5月6日の投稿でマーティナスがそれを打ち付けました。
int fact(int n) {
if(n==0) return 1;
else return n * fact(n-1);
}
上記の実装を読んで理解してください。
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)))))
私の意見では、Javaの初心者レベルの知識を持つ人の意見であるため、n == 1をn <= 1または(n == 0)||(n == 1)に変更することをお勧めします0の階乗は1だからです。
正しいものは次のとおりです。
int factorial(int n)
{
if(n==0||n==1)
return 1;
else
return n*factorial(n-1);
}
これは階乗0に対して1を返します。私を信じてください。私はこれを難しい方法で学びました。 0の条件を維持しないために、インタビューをクリアできませんでした。
私見、再帰関連のアクションを理解するためのキーは次のとおりです。
func(n-1);
)。n == 0
)、再帰が停止し、メソッドは実際の作業を行い、値を上位スタックの呼び出し元メソッドに返し、スタックの最上部にバブリングします。確かに、メソッドは再帰に飛び込む前(スタックの最上部から最下部)またはその途中で有用な作業を行うことができます。