再帰について見つけることができるほとんどすべての記事には、階乗またはフィボナッチ数の例が含まれています。
再帰を教えるための興味深い数学以外のcodeの例はありますか?
私は分割統治アルゴリズムを考えていますが、それらは通常、複雑なデータ構造を伴います。
ディレクトリ/ファイル構造は、再帰の使用の最良の例です。開始する前に誰もがそれを理解しているためですが、ツリーのような構造を含むものであれば何でも可能です。
void GetAllFilePaths(Directory dir, List<string> paths)
{
foreach(File file in dir.Files)
{
paths.Add(file.Path);
}
foreach(Directory subdir in dir.Directories)
{
GetAllFilePaths(subdir, paths)
}
}
List<string> GetAllFilePaths(Directory dir)
{
List<string> paths = new List<string>();
GetAllFilePaths(dir, paths);
return paths;
}
ツリー構造に関係するものを探します。ツリーは比較的理解しやすく、リストなどの線形データ構造よりもはるかに早く再帰的ソリューションの美しさが明らかになります。
考慮すべき事柄:
これらはすべて実際の実際のシナリオに関連しており、実際に重要なアプリケーションで使用できます。
https://stackoverflow.com/questions/105838/real-world-examples-of-recursion
https://stackoverflow.com/questions/2085834/how-did-you-practically-use-recursion
https://stackoverflow.com/questions/126756/examples-of-recursive-functions
ここに私の頭に浮かぶいくつかのより実用的な問題があります:
QuickSortは、最初に思い浮かぶものです。バイナリ検索も再帰的な問題です。それ以外にも、再帰の作業を開始すると、解決策がほぼ無料で失敗する問題のクラス全体があります。
Pythonで再帰的に定義されたソート。
def sort( a ):
if len(a) == 1: return a
part1= sort( a[:len(a)//2] )
part2= sort( a[len(a)//2:] )
return merge( part1, part2 )
再帰的に定義されたマージ。
def merge( a, b ):
if len(b) == 0: return a
if len(a) == 0: return b
if a[0] < b[0]:
return [ a[0] ] + merge(a[1:], b)
else:
return [ b[0] ] + merge(a, b[1:])
再帰的に定義された線形検索。
def find( element, sequence ):
if len(sequence) == 0: return False
if element == sequence[0]: return True
return find( element, sequence[1:] )
再帰的に定義された二分探索。
def binsearch( element, sequence ):
if len(sequence) == 0: return False
mid = len(sequence)//2
if element < mid:
return binsearch( element, sequence[:mid] )
else:
return binsearch( element, sequence[mid:] )
ある意味で、再帰とはすべてのソリューションを分割して征服することです。つまり、問題のスペースを分割して単純な問題のソリューションを見つけ、通常は元の問題を再構築して正しい答えを作成します。
再帰を教える数学を含まないいくつかの例(少なくとも私が大学時代から覚えている問題):
これらは、問題を解決するためにバックトラッキングを使用する例です。
その他の問題は、人工知能ドメインの古典的なものです:深さ優先検索、パスファインディング、計画の使用。
これらすべての問題には、ある種の「複雑な」データ構造が関係しますが、数学(数値)でそれを教えたくない場合は、選択肢が制限される可能性があります。 Yoyは、リンクされたリストのような基本的なデータ構造で教えることを始めたいと思うかもしれません。たとえば、リストを使用して自然数を表すには:
0 =空のリスト1 = 1つのノードを持つリスト。 2 = 2ノードのリスト。 ...
次に、次のようにこのデータ構造の観点から2つの数値の合計を定義します:空+ N = N Node(X)+ N = Node(X + N)
ハノイの塔は、再帰を学ぶのに役立つものです。
Webには、さまざまな言語で多くの解決策があります。
パリンドローム検出器:
文字列で開始: "ABCDEEDCBA"開始文字と終了文字が等しい場合、再帰して "BCDEEDCB"などを確認します。
二分探索アルゴリズムはあなたが望むもののように聞こえます。
関数型プログラミング言語では、高次関数が使用できない場合、変更可能な状態を回避するために、命令ループの代わりに再帰が使用されます。
F#は、両方のスタイルを許可する不純な関数型言語なので、ここで両方を比較します。次のリストのすべての数値を合計します。
可変変数を含む命令ループ
let xlist = [1;2;3;4;5;6;7;8;9;10]
let mutable sum = 0
for x in xlist do
sum <- sum + x
変更可能な状態のない再帰ループ
let xlist = [1;2;3;4;5;6;7;8;9;10]
let rec loop sum xlist =
match xlist with
| [] -> sum
| x::xlist -> loop (sum + x) xlist
let sum = loop 0 xlist
この種類の集計isは高次関数List.fold
でキャプチャされ、List.fold (+) 0 xlist
として、または便利な関数List.sum
だけでList.sum xlist
と書くこともできます。
私はゲームプレイAIで再帰を多用しました。 C++で作成する場合、お互いを順番に呼び出す一連の約7つの関数を使用しました(最初の関数にはそれらすべてをバイパスし、代わりに2つ以上の関数のチェーンを呼び出すオプションがあります)。どちらかのチェーンの最後の関数は、検索したい残りの深さが0になるまで最初の関数を再度呼び出しました。この場合、最後の関数は評価関数を呼び出し、位置のスコアを返します。
複数の機能により、プレイヤーの決定またはゲーム内のランダムなイベントに基づいて簡単に分岐することができました。非常に大きなデータ構造を渡していたので、可能な限り参照渡しを使用しましたが、ゲームの構造が原因で、検索で「元に戻す」ことは非常に困難でした。一部の関数で値渡しを使用して、元のデータを変更しないようにします。このため、再帰的なアプローチではなくループベースのアプローチに切り替えるのは非常に難しいことが判明しました。
この種のプログラムの非常に基本的な概要を見ることができます https://secure.wikimedia.org/wikipedia/en/wiki/Minimax#Pseudocode を参照してください
ビジネスにおける実に優れた実例は、「Bill of Materials」と呼ばれるものです。これは、完成品を構成するすべてのコンポーネントを表すデータです。たとえば、自転車を使用してみましょう。自転車には、ハンドルバー、ホイール、フレームなどがあります。これらの各コンポーネントには、サブコンポーネントを含めることができます。たとえば、ホイールにはスポーク、バルブステムなどを含めることができます。したがって、通常、これらはツリー構造で表されます。
ここで、BOMに関する集計情報を照会したり、BOMの要素を変更したりするために、多くの場合、再帰に頼ります。
class BomPart
{
public string PartNumber { get; set; }
public string Desription { get; set; }
public int Quantity { get; set; }
public bool Plastic { get; set; }
public List<BomPart> Components = new List<BomPart>();
}
そして、再帰呼び出しのサンプル...
static int ComponentCount(BomPart part)
{
int subCount = 0;
foreach(BomPart p in part.Components)
subCount += ComponentCount(p);
return part.Quantity * Math.Max(1,subCount);
}
明らかに、BomPartクラスにはさらに多くのフィールドがあります。プラスチックコンポーネントの数、完全なパーツを作成するのにかかる労力などを把握する必要があるかもしれません。これはすべて、ツリー構造での再帰の有用性に戻ります。