再帰が使用されるすべての場所でfor
ループを使用できると言うのは正しいですか?そして、再帰が通常遅い場合、for
ループ反復でこれを使用する技術的な理由は何ですか?
そして、再帰をfor
ループに変換することが常に可能な場合、それを行うための経験則はありますか?
再帰が使用されるすべての場所でfor
ループを使用できると言うのは正しいですか?そして、再帰が通常遅い場合、for
ループ反復でこれを使用する技術的な理由は何ですか?
そして、再帰をfor
ループに変換することが常に可能な場合、それを行うための経験則はありますか?
回答:
すべての関数呼び出しは、呼び出し元の関数に戻ることができるようにスタックに格納する必要があるため、通常、再帰ははるかに遅くなります。多くの場合、スコープ分離を実装するには、メモリを割り当ててコピーする必要があります。
テールコール最適化などのいくつかの最適化再帰を高速化しますが、常に可能であるとは限らず、すべての言語で実装されているわけではありません。
再帰を使用する主な理由は、
もちろん、すべての再帰は一種のループとしてモデル化できます。それがCPUが最終的に行うことです。そして再帰自体は、より直接的には、関数呼び出しとスコープをスタックに入れることを意味します。ただし、再帰アルゴリズムをループするアルゴリズムに変更すると、多くの作業が必要になり、コードの保守性が低下する可能性があります。すべての最適化について、プロファイリングまたは証拠によって必要であることが示された場合にのみ試行する必要があります。
再帰が使用されるすべての場所でforループを使用できると言うのは正しいですか?
はい、ほとんどのCPUの再帰はループとスタックデータ構造でモデル化されているためです。
そして、再帰が通常遅い場合、それを使用する技術的な理由は何ですか?
「通常は遅い」わけではありません。誤って適用されるのは再帰であり、遅いです。その上、最近のコンパイラーは、要求せずに一部の再帰をループに変換するのが得意です。
そして、再帰をforループに変換することが常に可能な場合、それを行うための経験則はありますか?
反復的に説明するときに最もよく理解されるアルゴリズムの反復プログラムを作成します。再帰的に最もよく説明されるアルゴリズムの再帰プログラムを記述します。
たとえば、多くのプログラミング言語でのバイナリツリーの検索、クイックソートの実行、式の解析については、再帰的に説明されることがよくあります。これらも再帰的にコーディングするのが最適です。一方、階乗の計算とフィボナッチ数の計算は、反復の観点から説明する方がはるかに簡単です。それらに再帰を使用することは、大ハンマーでハエを叩くようなものです:大ハンマーがそれで本当に良い仕事をするときでも、それは良い考えではありません+。
そして、もし再帰が通常遅い場合、ループ反復のために再帰を使用する技術的な理由は何ですか?
アルゴリズムによっては、繰り返し解くことが難しいためです。深さ優先検索を再帰的および反復的に解決するようにしてください。反復でDFSを解決するのは非常に難しいという考えがわかります。
試してみるもう1つの良い点:Mergeの並べ替えを反復的に記述してみてください。かなり時間がかかります。
再帰が使用されるすべての場所でforループを使用できると言うのは正しいですか?
はい。このスレッドは、に対する非常に良い答えがあります。
そして、再帰をforループに変換することが常に可能な場合、それを行うための経験則はありますか?
私を信じて。独自のバージョンを作成して、深さ優先検索を繰り返し解決してください。一部の問題は、再帰的に解決する方が簡単であることがわかります。
ヒント:分割統治法で解決できる問題を解決する場合は、再帰が適しています。
反復を使用して同等のメソッドを作成するには、明示的にスタックを使用する必要があります。反復バージョンのソリューションにはスタックが必要であるという事実は、問題が再帰の恩恵を受けることができるほど難しいことを示しています。原則として、再帰は、固定量のメモリでは解決できないため反復的に解決するときにスタックが必要になる問題に最適です。そうは言っても、再帰と反復は、異なるパターンに従っても同じ結果を示す可能性があります。どちらの方法がより適切に機能するかを判断するのはケースバイケースであり、ベストプラクティスは、問題が続くパターンに基づいて選択することです。
たとえば、三角形シーケンスのn番目の三角形番号を見つけるには、次のようにします。1 3 6 10 15…反復アルゴリズムを使用してn番目の三角形番号を見つけるプログラム:
反復アルゴリズムを使用する:
//Triangular.java
import java.util.*;
class Triangular {
public static int iterativeTriangular(int n) {
int sum = 0;
for (int i = 1; i <= n; i ++)
sum += i;
return sum;
}
public static void main(String args[]) {
Scanner stdin = new Scanner(System.in);
System.out.print("Please enter a number: ");
int n = stdin.nextInt();
System.out.println("The " + n + "-th triangular number is: " +
iterativeTriangular(n));
}
}//enter code here
再帰アルゴリズムを使用する:
//Triangular.java
import java.util.*;
class Triangular {
public static int recursiveTriangular(int n) {
if (n == 1)
return 1;
return recursiveTriangular(n-1) + n;
}
public static void main(String args[]) {
Scanner stdin = new Scanner(System.in);
System.out.print("Please enter a number: ");
int n = stdin.nextInt();
System.out.println("The " + n + "-th triangular number is: " +
recursiveTriangular(n));
}
}
答えのほとんどは、iterative
=であることを前提としているようですfor loop
。あなたのforループが制限されていない場合は(ラ・ Cを、あなたはあなたのループカウンタでやりたいことができます)、それは正しいです。それが実際の for
ループである場合(たとえば、Pythonまたはループカウンターを手動で変更できないほとんどの関数型言語の場合)、それは正しくありません。
すべての(計算可能な)関数は、再帰的に実装することも、while
ループ(または条件付きジャンプ、基本的に同じもの)を使用して実装することもできます。本当に自分をfor loops
に制限すると、それらの関数のサブセットのみが取得されます(基本的な操作が妥当な場合は、プリミティブな再帰関数です)。確かに、これはかなり大きなサブセットであり、実際に使用する可能性のあるすべての機能が含まれています。
さらに重要なことは、多くの関数は再帰的に実装するのが非常に簡単で、反復的に実装するのが非常に難しいことです(手動でのコールスタックの管理はカウントされません)。
はい、タナクロン・タンダヴァスが言ったように、
再帰は、分割統治法で解決できる問題を解決する場合に適しています。
例:ハノイの塔
私のコンピューターサイエンスの教授が、再帰的な解決策を持つすべての問題には反復的な解決策もあると言ったのを覚えているようです。彼は、再帰的ソリューションは通常遅いと言いますが、反復的ソリューションよりも推論とコーディングが容易な場合に頻繁に使用されます。
ただし、より高度な再帰的ソリューションの場合、単純なfor
ループを使用して常にそれらを実装できるとは思いません。
再帰+暗記は、純粋な反復アプローチと比較してより効率的なソリューションにつながる可能性があります。たとえば、これを確認してください:http : //jsperf.com/fibonacci-memoized-vs-iterative-for-large-n
recursion
対iteration
?iteration = for loop
おもう。