この記事では主に Java の反復と 再帰 について紹介します。 では Java の反復と再帰をそれぞれ紹介し、その後、反復と再帰の違いと数値再帰の関連内容を紹介します。詳しく紹介していますので、困っている方は参考にしていただければと思います。
まえがき
最近、本を読んでいたときにこの内容を目にして、なかなか読み応えがあると感じました。反復ではループ (for、while、do...wile) または反復子を使用し、ループ条件が満たされない場合に終了します。再帰 (通常は関数の再帰) は、それ自体を呼び出すことも、間接呼び出しにすることもできます。つまり、メソッド A がメソッド B を呼び出し、次にメソッド B がメソッド A を呼び出します。再帰終了の条件は、if、else ステートメント 、when です。条件が満たされると終了します。
上記は、Java における反復と再帰の構文の特徴です。詳細については、以下の記事をご覧ください。1. 再帰
反復に関しては、数学的な式について言及する必要があります: n!=n*(n-1)*(n-2)*... *1
n!=n*(n-1)*(n-2)*...*1
有很多方法来计算阶乘。有一定数学基础的人都知道n!=n*(n-1)!
因此,代码的实现可以直接写成:
代码一
int factorial (int n) { if (n == 1) { return 1; } else { return n*factorial(n-1); } }
在执行以上代码的时候,其实机器是要执行一系列乘法的: factorial(n)
→ factorial(n-1)
→ factorial(n-2)
→ … → factorial(1)
階乗を計算する方法はたくさんあります。一定の数学的基礎を持つ人なら誰でも n!=n*(n-1)!
を知っているため、コードの実装は次のように直接書くことができます:
コード 1int factorial (int n) {
int product = 1;
for(int i=2; i<n; i++) {
product *= i;
}
return product;
}
factorial(n)
→ factorial(n-1)
→ factorial(n-2)
→ … → factorial(1)
。したがって、継続的に追跡 (最後の計算の結果を追跡) し、計算のために乗算を呼び出す (乗算チェーンを構築する) 必要があります。自分自身を継続的に呼び出すこのタイプの操作は、再帰と呼ばれます。再帰はさらに線形再帰と数値再帰に分類できます。アルゴリズムの入力に対して情報量が線形に増加する再帰を線形再帰といいます。 n! (階乗) の計算は線形再帰です。 N が増加すると、計算に必要な時間が直線的に増加するためです。入力が増加するにつれて指数関数的に増加する別のタイプの情報は、ツリー再帰と呼ばれます。
2. 反復
n を計算するもう 1 つの方法は、まず 1 に 2 を掛け、次にその結果に 4 を掛けて、N になるまで掛けます。プログラムを実装するときに、乗算が実行されるたびに、カウンター値が N に等しくなるまでカウンターを定義できます。コードは次のとおりです。 コード 2
int fib (int n) { if (n == 0) { return 0; } else if (n == 1) { return 1; } else { return fib(n-1) + fib(n-2); } }
コード 1 と比較して、コード 2 は乗算チェーンを構築しません。計算の各ステップを実行するとき、必要なのは現在の結果 (積) と i の値だけです。この形式の計算は反復と呼ばれます。反復にはいくつかの条件があります: 1. 初期値を持つ 変数 がある。 2. 変数値がどのように更新されるかを記述するルール。 3. 終了条件。 (ループの 3 つの要素: ループ変数、ループ本体、ループ終了条件)。再帰と同じです。入力に応じて線形に増加する時間要件を線形反復と呼ぶことができます。
3. 反復 VS 再帰
2 つのプログラムを比較すると、特に数学的関数の点でほぼ同じであることがわかります。 n! を計算する場合、その計算ステップ数は n の値に比例します。ただし、プログラムの観点からそれらがどのように動作するかを考えると、これら 2 つのアルゴリズムは大きく異なります。
(注: 元の記事は違いについて少しばかげているので、ここでは翻訳しません。以下は著者独自の要約です。)
最初に再帰を分析します。 実際、再帰の最大の利点は次のとおりです。複雑なアルゴリズムを、反復可能ないくつかの同一のアルゴリズムに分解します。したがって、再帰を使用して計算ロジックを実装すると、多くの場合、短いコードのみで解決できるため、そのようなコードは比較的理解しやすいです。ただし、再帰は多くの関数呼び出しを意味します。関数呼び出しのローカル状態がスタックを使用して記録される理由。したがって、これにより多くのスペースが無駄になる可能性があり、再帰が深すぎる場合はスタック オーバーフローが発生する可能性があります。 次に、反復を分析します。実際、再帰は反復に置き換えることができます。しかし、単純で理解しやすい再帰と比較すると、反復はより単純で理解しにくいです。特に、より複雑なシーンに遭遇した場合。ただし、コードを理解することの難しさによってもたらされる欠点も明らかです。反復は再帰よりも効率的であり、消費するスペースも少なくなります。
再帰には反復がなければなりませんが、反復には再帰が存在しない可能性があり、それらのほとんどは相互に変換できます。
反復を使用できる場合は、再帰呼び出し関数を使用しないでください。再帰呼び出しがスペースを無駄にするだけでなく、再帰が深すぎるとスタック オーバーフローが発生しやすくなります。
🎜4. 数値の再帰🎜🎜🎜前面介绍过,树递归随输入的增长的信息量呈指数级增长。比较典型的就是斐波那契数列:
用文字描述就是斐波那契数列中前两个数字的和等于第三个数字:0,1,1,2,3,5,8,13,21……
递归实现代码如下:
int fib (int n) { if (n == 0) { return 0; } else if (n == 1) { return 1; } else { return fib(n-1) + fib(n-2); } }
计算过程中,为了计算fib(5)
,程序要先计算fib(4)
和 fib(3)
,要想计算fib(4)
,程序同样需要先计算 fib(3)
和 fib(2)
。在这个过程中计算了两次fib(3)。
从上面分析的计算过程可以得出一个结论:使用递归实现斐波那契数列存在冗余计算。
就像上面提到的,可以用递归的算法一般都能用迭代实现,斐波那契数列的计算也一样。
int fib (int n) { int fib = 0; int a = 1; for(int i=0; i<n; i++) { int temp = fib; fib = fib + a; a = temp; } return fib; }
虽然使用递归的方式会有冗余计算,可以用迭代来代替。但是这并不表明递归可以完全被取代。因为递归有更好的可读性。
以上がJava での反復と再帰の例の詳細な説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。