それで私はここで勉強していて、再帰の全体のポイントが問題を複数の小さな問題に分割することであるかどうか考えていました、それらの問題が並行して解決されたらどうでしょうか?クイック検索により、 this のようなリンクが表示されます。ここでは、コンテキストの切り替えが理にかなっているため、価値がないと人々は言いますが、多数のスレッドを生成する代わりに、固定数のスレッドを保持して、たとえばスタックのようなデータ構造への再帰呼び出しの状態、それはそのような再帰を使用するのは良い考えでしょうか?それがタスクに依存している場合、それをどのようにするのですか?それは良いアプローチか悪いアプローチですか?
再帰は、問題を小さな問題に分割したり、同時実行を可能にすることではありません。 自己相似問題を解決することです。
二分探索木 のこの表現を考えてみましょう:
public class Node
{
public string key;
public Node left;
public Node right;
}
バイナリ検索ツリーを完全に表すために必要なのはこれだけです。どうして? Recursive Data Type なので、 left
およびright
メンバーのノードはそれぞれ新しいツリーのルートを形成し、これらのメンバーはそれぞれ新しいツリーのルートを形成します。
バイナリ検索ツリーで何かを見つける方法は次のとおりです。
public Node Find(Node node, string key)
{
if (node == null) return null;
if (node.key == key) return node;
if (node.key < key) return Find(node.left, key);
else return Find(node.right, key);
}
シンプルですよね?
これはどの程度うまく機能しますか?さて、32回以下の再帰を使用して、40億のノードを含むバランスの取れたバイナリツリーで任意の項目を見つけることができます。
これは、分割統治問題を並行して実行する問題のように聞こえます。
人々はSOで実際の実装をいくつか行っており、彼らの戦略は、特定の深さに達した後に順次計算を実装し始めることです。生成されるスレッドの最大数は順次の深さの制限に依存するため、これはスレッド数が制限されている例と同様です。
ここで彼らの投稿を見つけてください: https://stackoverflow.com/questions/16260879/how-to-parallelize-a-divide-and-conquer-algorithm-efficiently
編集:そこにあるコメントの多くは、この戦略が効果的であるかどうかについても不満があるようですが、スレッド化のためにオーバーヘッドが発生することは避けられません。
再帰と並列処理を使用できますが、解決する問題によって異なります。再帰メソッドが1回の実行で2回以上selfを呼び出す場合は、スレッドを使用してこれを解決できます。しかし、それを自分自身と1回だけ呼び出すと、階乗など、意味がなくなります。階乗はすべての実行でn-1の結果を期待するため、並列処理を使用してもこの場合のパフォーマンスは向上しません。
他のものに追加するには:
何かが再帰的だからといって、それが自動的に高速になったり、メモリ使用量が少なくなったりするわけではありません。実際、プログラムの速度が大幅に低下する可能性があります。例としてフィボナッチ数列を取り上げます。これらの純粋な再帰を行う場合、次のようなものです:
int fibonacci( int n) {
if (n = 0 || n = 1) return 1;
return fibonacci(n-1) + fibonacci(n-2);
}
これは2になりますん 関数呼び出し-20番目のフィボナッチ数を計算するだけで、この関数は100万回以上呼び出されます。
代わりに、以前の数値を保存して繰り返し(または動的に)解決した場合、必要なのはループの「実行」数がnだけです-これは線形です!