ホームページ >システムチュートリアル >Linux >ソフトウェア開発における再帰操作

ソフトウェア開発における再帰操作

WBOY
WBOYオリジナル
2024-08-16 19:54:301190ブラウズ

ソフトウェア開発における再帰操作

この古典的な再帰階乗を見てみましょう:

リーリー

再帰階乗 -階乗.c
関数が自分自身を呼び出すという概念は、最初は理解するのが難しいです。このプロセスをより鮮明かつ具体的にするために、次の図は、factorial(5) が呼び出され、コード行 n == 1 に到達したときのスタック上のエンドポイントの状況を示しています。

ソフトウェア開発における再帰操作

factorial を呼び出すたびに、新しいスタック フレームが生成されます。これらのスタック フレームの作成と破棄により、再帰バージョンは対応する反復バージョンよりも大幅に遅くなります。これらのスタック フレームが蓄積すると、呼び出しが返される前にスタック領域が使い果たされ、プログラムがクラッシュする可能性があります。

これらの心配は理論上存在することがよくあります。たとえば、スタック フレームは階乗ごとに 16 バイトかかります (これはスタックの配置やその他の要因によって異なる場合があります)。コンピューター上で最新の x86 Linux カーネルを実行している場合、通常は 8 GB のスタック領域があるため、階乗プログラムの n は最大約 512,000 になる可能性があります。これは巨大な結果であり、それを表現するには 8,971,833 ビットが必要であるため、スタック スペースはまったく問題になりません。スタック スペースには小さな整数 (64 ビット整数であっても) が格納されており、これまでに何千回もオーバーフローしました。なくなった。

CPU の使用についてはしばらく見ていきます。ここでは、ビットとバイトから一歩下がって、再帰を一般的なテクノロジーとして扱いましょう。私たちの階乗アルゴリズムは、要約すると、整数 N、N-1、… 1 をスタックにプッシュし、それらを逆の順序で乗算することです。これを実現するために実際にはプログラム呼び出しスタックを使用します。その詳細は次のとおりです。スタックをヒープ上に割り当てて使用します。コール スタックには特別な特性がありますが、必要に応じて使用できる単なるデータ構造の 1 つです。この図がこれを理解するのに役立つことを願っています。

呼び出しスタックをデータ構造として考えると、整数を積み上げてから乗算するのは良い考えではないことが明確になります。これは欠陥のある実装です。釘を打つためにドライバーを使用するようなものです。階乗を計算するには反復プロセスを使用する方が合理的です。

ただし、ネジが多すぎて 1 つしか選択できません。迷路の中にネズミがいて、ネズミがチーズを見つけるのを手伝うという古典的な面接の質問があります。ネズミが迷路内で左または右に曲がることができると仮定します。この問題を解決するためにどのようにモデル化しますか?

現実の多くの問題と同様、二分木の各ノードが迷路内の位置を表すように、チーズを探すネズミの問題をグラフに単純化することができます。その後、可能な限りマウスを左に回転させ、行き止まりに達したら後戻りして再び右に回転させることができます。以下は迷路を歩くネズミの例です:

ソフトウェア開発における再帰操作

エッジ(線)に到達するたびに、マウスを左または右に回転させて新しい位置に到達します。どの方向に曲がろうともブロックされている場合は、該当するエッジが存在しないことを意味します。さあ、話し合ってみましょう!このプロセスは、コール スタックを使用するか他のデータ構造を使用するかに関係なく、再帰プロセスから切り離すことができません。コールスタックの使用は非常に簡単です:

リーリー

再帰的迷路解決ダウンロード

maze.c:13 でチーズを見つけると、スタックは次のようになります。また、コマンドを使用して収集されたデータである GDB 出力で、より詳細なデータを確認することもできます。

ソフトウェア開発における再帰操作

これは再帰を使用するのに適した問題であるため、再帰の良好な動作を示しています。それは驚くべきことではありません。アルゴリズムに関して言えば、再帰は例外ではなく原則です。これは、検索時、ツリーやその他のデータ構造の横断時、解析時、並べ替えが必要なときなど、あらゆる場所に表示されます。 pi や e は宇宙のすべての基礎であるため、数学では「神」として知られていますが、再帰も同じです。それは計算の構造の中に存在するだけです。

スティーブン・スキエナの優れた著書『アルゴリズム設計ガイド』の素晴らしい点は、彼が現実世界の問題解決の背後にあるアルゴリズムを実証する手段として「戦争物語」を通じて自分の作品を解釈していることです。これは、アルゴリズムの知識を広げるために私が知る限り最高のリソースです。もう 1 つの読み物は、LISP 実装に関する McCarthy のオリジナルの論文です。再帰はその名前であり、言語におけるその基本原理でもあります。論文は読みやすく興味深いものであり、巨匠の仕事を見るのはとても楽しいです。

미로 문제로 돌아갑니다. 여기서 재귀를 남기기는 어렵지만, 반드시 콜스택을 통해서 이루어져야 한다는 뜻은 아닙니다. RRLL과 같은 문자열을 사용하여 회전을 추적한 다음 이 문자열을 사용하여 마우스의 다음 움직임을 결정할 수 있습니다. 또는 치즈 찾기의 전체 상태를 기록하기 위해 다른 것을 할당할 수도 있습니다. 여전히 재귀 프로세스를 구현하므로 자신만의 데이터 구조를 구현하기만 하면 됩니다.

스택 호출이 더 적절하기 때문에 더 복잡해 보입니다. 각 스택 프레임은 현재 노드뿐만 아니라 해당 노드의 계산 상태도 기록합니다(이 경우 왼쪽으로만 놔두었는지 아니면 오른쪽으로 가려고 했는지 여부). 따라서 코드는 중요하지 않게 되었습니다. 그러나 때때로 우리는 오버플로에 대한 두려움과 기대되는 성능 때문에 이 우수한 알고리즘을 포기합니다. 그건 바보야!

보시다시피 스택 공간은 매우 크고 스택 공간이 소진되기 전에 다른 제한 사항에 직면하는 경우가 많습니다. 한편으로는 문제의 크기를 확인하여 안전하게 처리할 수 있는지 확인할 수 있습니다. CPU 문제는 널리 유포된 두 가지 문제 사례, 즉 멍청한 계승과 끔찍한 메모리 없는 O(2n) 피보나치 재귀에 의해 발생합니다. 이는 스택 재귀 알고리즘을 올바르게 표현한 것이 아닙니다.

사실 스택 작업은 매우 빠릅니다. 일반적으로 데이터에 대한 스택 오프셋은 매우 정확하고 캐시의 핫 데이터이며 특수 명령이 이에 대해 작동합니다. 동시에 힙에 할당된 자체 데이터 구조를 사용하는 것과 관련된 오버헤드도 상당합니다. 사람들이 스택 호출 재귀보다 더 복잡하고 성능이 떨어지는 구현 방법을 작성하는 경우가 종종 있습니다. 마지막으로 최신 CPU의 성능은 매우 좋으며 일반적으로 CPU는 성능 병목 현상을 일으키지 않습니다. 항상 프로그램 성능과 해당 성능 측정을 고려하는 것처럼 프로그램 단순성을 희생하는 것을 고려할 때 주의하십시오.

다음 글은 스택 탐색 시리즈의 마지막 글이 될 것입니다. 테일 콜, 클로저 및 기타 관련 개념에 대해 알아보겠습니다. 그런 다음, 우리의 오랜 친구인 Linux 커널에 대해 알아볼 시간입니다. 읽어주셔서 감사합니다!

ソフトウェア開発における再帰操作

以上がソフトウェア開発における再帰操作の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。