回 2-5 道 2 データ構造とアルゴリズム
再帰は、
読み終えたら再帰の
この頁の流れ
再帰の
自分を 呼ぶ関数は、 なぜ止まって 答えを 返せるのか
再帰の
2026年10月26日に
フォルダの
この
再帰は どう動くのか
再帰の
2 つの 決まり、 止まる 条件と 小さくする一歩
- 止まる
条件 (基底条件)。 これ以上小さく せず、 すぐ 答えを 返す場合。 階乗なら 「1 の 階乗は 1」 - 小さく
する 一歩。 問題を 少し 小さくして 自分を 呼び、 その 答えから 今の 答えを 作る。 階乗なら 「n の 階乗は、 n に n − 1 の 階乗を 掛けた物」
英語では
一歩ごとに、
階乗の再帰
def fact(n):
if n == 1: # 止まる条件
return 1
return n * fact(n - 1)呼ぶたびに、 途中の 状態が積まれる
関数を
fact(4) の 4 * fact(3) は、fact(3) のfact(4) はfact(3) が
| 段 | 積まれた呼び出し | 待っている計算 |
|---|---|---|
| 4 (一番上) | fact(1) | なし。 |
| 3 | fact(2) | 2 × fact(1) |
| 2 | fact(3) | 3 × fact(2) |
| 1 (一番下) | fact(4) | 4 × fact(3) |
止まる 条件に 着いてから、 上から 順に 値が決まる
止まるfact(1) がfact(2) が 2 × 1 をfact(4) が
再帰は、
「停止条件を
動かして確かめる
1 本目は、calls は、最初の fact(4) も
動かす 階乗の呼ぶと戻るを字下げで出す
▶ 実行ボタンを押すと結果がここに表示されます
実行すると、次の通りに出ます
呼ぶ f(4)
呼ぶ f(3)
呼ぶ f(2)
呼ぶ f(1)
戻る 1
戻る 2
戻る 6
戻る 24
答え 24
呼んだ回数 4上のf(1) がf(4) が
紙で
2 本目は、
動かす 合計、入れ子を平らに、木の大きさ
▶ 実行ボタンを押すと結果がここに表示されます
実行すると、次の通りに出ます
10
[1, 2, 3, 4, 5]
70
65total は、
flat は、[1, 2, 3, 4, 5] に
size は、img だけなら
total にはxs[1:] は
再帰は どこで 崩れるのか
再帰は、RecursionError という
止まる 条件が 抜けると、 上限まで 積み上がる
止まるRecursionError を
上限は sys.getrecursionlimit() で読み、sys.setrecursionlimit() で
次の
動かす 止まる条件が無いと上限で止まる
▶ 実行ボタンを押すと結果がここに表示されます
実行すると、次の通りに出ます
200 RecursionError
400 RecursionError
深さの差 200
RuntimeError の仲間 Trueどちらも RecursionError で
RecursionError は RuntimeError のRuntimeError でした。
入れ子が 深すぎる 入力は、 ループに 書き換える
止まる
再帰は、flat とflat_loop で
動かす 深い入れ子を再帰とループで平らに
▶ 実行ボタンを押すと結果がここに表示されます
実行すると、次の通りに出ます
再帰 RecursionError
ループ [7]
[1, 2, 3, 4, 5]再帰の flat はflat_loop はtodo に
階乗のように
階乗をループで書く
def fact_loop(n):
r = 1
for k in range(2, n + 1):
r *= k
return r自分を
再帰は 仕事で どう 確かめるのか
仕事では、
- 関数の
頭に、 自分を 呼ばずに返す ifがあるか。 空の リスト、 0、 1、 ファイルなど、 一番小さい 入力で 何を返すか - 引数が
減る、 リストが 短くなる、 木の 下へ 進むなど、 一歩ごとに 近づくか。 同じ 所へ 戻る 輪が無いか - 外から
来る JSON、 XML、 パス、 入れ子の リストは、 深さに 上限を 決めて 断るか、 自前の スタックの ループで処理する - 試験に、
止まる 条件 その もの、 1 段の 入力、 深い 入力の 3 つを入れる
試験の例
def test_flat():
assert flat([]) == []
assert flat([1]) == [1]
box = 7
for i in range(20000):
box = [box]
assert flat_loop(box) == [7]関わる実例
Go の XML の 読み取りが、 深い 入れ子で スタックを 使い 切った (2022年7月12日に修正)
Go のencoding/xml では、Decoder.Skip が
崩れた
出典は GO-2022-0521 (Go の
Go の Glob が、 区切りの 多い パスで スタックを 使い 切った (2022年7月12日に修正)
同じ日には、path/filepath の Glob (パスの
崩れた
出典は GO-2022-0522 (Go の
よくある問い
再帰と スタックオーバーフローは 同じですか
違います。RecursionError で
再帰は なぜ遅いと 言われるのですか
呼ぶたびに
再帰と ループは どちらを 使えば よいですか
木や
末尾再帰とは 何ですか、 Python では 速くなりますか
末尾再帰は、
確認クイズ
答えだと
1 問目
この
呼ぶ前と後で出す
def g(n):
if n == 0:
return
print("前", n)
g(n - 1)
print("後", n)
g(2)2 問目
fact(4) がfact(1) にfact がfact(4) も
3 問目
止まる
4 問目
外から
説明課題
fact(3) を
自分の
この回の 先の一歩
入れ子の
もっと深く
この
一次資料
どれも
- sys (Python 公式文書、英語)。
getrecursionlimit()とsetrecursionlimit()、上限がC の スタックの あふれを 防ぐ こと、 高すぎると 落ちること - Built-in Exceptions (Python 公式文書、英語)。
RecursionErrorがRuntimeErrorの子で、 3.5 より 前はただの RuntimeErrorだったこと - Tail Recursion Elimination (Python の
作者の 記事、英語)。2009年4月22日、 末尾再帰の 除去を 入れない理由 - GO-2022-0521 (Go の
脆弱性の 記録、英語)。CVE-2022-28131、 Decoder.Skipのスタックの枯渇 - encoding/xml の
Skip を 繰り返しに 直した 変更 (Go、英語)。2022年7月12日 - GO-2022-0522 (Go の
脆弱性の 記録、英語)。CVE-2022-30632、 Globのスタックの枯渇 - path/filepath の
Glob に 上限を 付けた 変更 (Go、英語)。区切りの 数の 上限、 2022年7月12日 - Release History (Go 公式、英語)。go1.18.4 と
go1.17.12 が 2022年7月12日に出たこと
次へ
道 2 データ構造とアルゴリズムの回
- 2-0アルゴリズムとは順次、
分岐、 反復の 3 つで書く手順頁で読む - 2-1線形探索と二分探索100 万件でも
20 回で見つかる頁で読む - 2-2バブルソートと
挿入・選択ソート遅い ソート 3 つ、 2 倍で 約 4 倍頁で読む - 2-3クイックソートと
マージソート速い ソート 2 つ、 平均と最悪頁で読む - 2-4スタックとキュー積む順と並ぶ順頁で読む
- 2-5再帰自分を
呼んで 小さくするこの回 - 2-6木と
二分探索木たどる 順、片寄り頁で読む - 2-7ハッシュテーブル鍵から
一気に 引く、衝突頁で読む - 2-8ダイクストラ法と最短経路点と
線、 幅優先、 重みのある道頁で読む - 2-9動的計画法同じ
計算を 2 度しない頁で読む - 2-10試験と
面接で確かめる基本情報 科目B、 道場 10 問、 面接 20 問頁で読む
