web-dev-qa-db-ja.com

再帰的なコードは非再帰的なコードよりも遅いですか?

現在、私は初心者プログラマーですが、プログラミングを勉強している私の友人が言うところによると、再帰コードは良いことであり、コードの重複が少なくなり、見栄えが良くなります。

今はそうかもしれませんが、再帰的なコードで手を試してみるたびに、それが非再帰的なコードよりも大幅に遅いことが常にわかりましたが、それが私がそれを使用した方法について何かであるかどうかはわかりませんが、私はそれを非常に疑っています。これをCollat​​z予想やフィボナッチ数生成のような単純なものに使用しましたが、通常の反復コードと比較した場合、再帰的アプローチは常に反復ソリューションの約2倍の時間でクロックします。

7

一部のアルゴリズムは自然に再帰的であり、再帰を使用して表現する方がはるかに簡単なため、少なくとも再帰がどのように機能するかを理解することは良いことです。

また、再帰アルゴリズム(または実装)は、本質的に反復アルゴリズムよりも低速ではありません。実際、すべての再帰アルゴリズムは、中間/一時値を自分で追跡する必要があるという犠牲を払って、同等の反復実装によって実装できます。再帰バージョンは、関数呼び出しスタック内の値を自動的に追跡します。

自然に再帰的なアルゴリズムの1つの例は、ツリーのすべてのノードに操作を適用する場合です。ここで最も自然な実装は、1つのノードで操作を実行し、ノードの子ごとに再帰的に呼び出す関数を持つことです。


フィボナッチ数列の計算など一部のアルゴリズムでは、再帰は自然に見えますが、再帰バージョンは同じ作業を何度も繰り返し実行するため、単純な実装は反復実装よりもはるかに遅くなります。これは、これらの特定のアルゴリズムでは、再帰が最良の選択ではない可能性があること、またはアルゴリズムの他の場所で再び必要になる可能性のある中間値を記憶するためにいくつかの手法を使用する必要があることを意味します。

他のアルゴリズム、特に分割統治法を使用するアルゴリズムの場合、反復解法で何かを追跡するためにスタックが必要であることがわかります。これらの場合、スタックの処理が暗黙的になるため、再帰バージョンの方がはるかにクリーンです。

再帰は遅くなり、スタックがいっぱいになる可能性があるため、より多くのメモリを消費します。しかし、 tail-calloptimization と呼ばれる回避策があります。これには、もう少し複雑なコードが必要です(渡すには、関数に別のパラメーターが必要なため)。スタック。

残念なことに C#はこれをサポートしていません ですから、大きな値のスタックオーバーフローが発生する可能性があるため、再帰を慎重に使用することをお勧めします。

結論として、関数型言語では再帰のみが必要であり、それに対処する必要があります。しかし、命令型言語では、反復はより自然で効率的です。最もよく使用される命令型言語の大部分は、Java(C#、Python)をサポートしていないか、または(C、C++)末尾呼び出しの最適化を保証していません。

6
m3th0dman