データ構造とアルゴリズム
第十二回
(2014年12月12日)
動的計画法
http://www.sw.it.aoyama.ac.jp/2014/DA/lecture12.html
Martin J. Dürst
© 2009-14 Martin
J. Dürst 青山学院大学
目次
- 前回の残り・まとめ
- アルゴリズムの設計方針
- 動的計画法の概要
- 応用例: 行列の連鎖乗算の順番
- Ruby による動的計画法
前回の残り・まとめ
(Boyer-Moore のアルゴリズム、文字列照合と文字コード)
アルゴリズムの設計方針
- 単純なアルゴリズム
- 分割統治法 (divide and conquer)
- 動的計画法 (dynamic programming)
動的計画法の概要
(dynamic programming)
- (最適) 解の構造を調査、明記
- (最適) 解の値を再帰的に定義
- ボトムアップで (最適) 解の値を算出
- 計算した情報から (最適) 解を構築
1950 年代ごろに Richard Bellman によって提唱
動的計画法の単純な例
- フィボナッチ関数 f(n) の定義:
- 0 ≦ n ≦ 1: f(n) =
n
- n ≧ 2: f(n) =
f(n-1) + f(n-2)
- 再帰的な定義のため、実装が簡単
- n が大きくなると実行が非常に遅い
- 遅さの理由: 同じ計算の繰り返し
(f(n) の計算の場合、f(1) は
f(n) 回に評価)
- 計算の順番の変更や途中結果の記憶で加速が可能
行列の乗算
- r0 × r1 の行列
0M1 と
- r1 × r2 の行列
1M2 の乗算
(0M1·
1M2 ⇒
0M1M2)
の結果は
- r0 × r2 の行列
0M2
- 乗算の計算量は
r0r1r2
の掛け算と
r0(r1-1)r2
の足し算で
O(r0r1r2)
- 掛け算と足し算の数がほぼ同等のため、掛け算の数を目安に
- 具体例: r0=100, r1=2,
r2=200
⇒掛け算の数: 100×2×200 = 40,000
三行列の連鎖乗算
乗算の順番の数
乗算の数 |
順番の数 |
0 |
1 |
1 |
1 |
2 |
2 |
3 |
5 |
4 |
14 |
5 |
42 |
6 |
132 |
7 |
429 |
8 |
1430 |
9 |
4862 |
- 最初少なく見えるが、爆発
- パスカルの三角形の中心の数 (1, 2, 6, 20, 70,...)
を整数 (1, 2, 3, 4, 5,...) で割ったもの
- カタラン数 (Catalan number)
Cn =
(2n)!/(n!(n+1)!)
= Ω(4n/n3/2)
- 応用が豊富:
- 括弧対の組み合わせの数
- 二分木の数
- (凸) 多角形の三角分割
乗算の最適な順番
- 総当たりで決めるのが無理
- 最低の計算コスト (スケーラ乗算の数、足し算は無視):
- mincost(a, c) は
aMc
の最低の計算コスト
- if a+1 ≧ c, mincost(a,
c) = 0
- if a+1 < c, mincost(a,
c) =
minc-1b=a+1
(cost(a, b, c))
- cost(a, b, c) は
aMbMc
の計算コスト
- aMc
の計算を分岐点 b で行うコスト
- cost(a, b, c) =
mincost(a, b) + mincost(b,
c) +
rarbrc
- Ruby による単純な実装: Cmatrix.rb の
MatrixSlow
最適化の順番と途中結果の記憶
- mincost(0, n) から再帰的に計算が可能
(下向き、トップダウン、top-down)
- 同じ mincost(x, y)
が何回も計算される
- 下から計算 (上向き、ボトムアップ、bottom-up):
- 長さ k (k が順番に 2,...,n)
の連鎖乗算の最低コストを計算
- 計算結果を記憶、再利用
- Ruby による実装: Cmatrix.rb の
MatrixPlan
計算の実例
|
0M1M5:
274
0M2M5: 450
0M3M5: 470
0M4M5: 320 |
|
|
0M1M4:
260
0M2M4: 468
0M3M4: 400 |
1M2M5:
366
1M3M5: 330
1M4M5:
250 |
|
|
0M1M3:
200
0M2M3: 288 |
1M2M4:
360
1M3M4:
220 |
2M3M5:
330
2M4M5: 390 |
|
|
0M1M2:
48 |
1M2M3:
120 |
2M3M4:
300 |
3M4M5:
150 |
|
0M1: 0 |
1M2: 0 |
2M3: 0 |
3M4: 0 |
4M5: 0 |
r0 = 4 |
r1 = 2 |
r2 = 6 |
r3 = 10 |
r4 = 5 |
r5 = 3 |
連鎖乗算の最適化の計算量
- mincost(a, c) の計算量は
O(c-a)
- 全ての mincost(a, a+k)
はO((n-k)·k)
- 合計の計算量は ∑nk=1
O((n-k)·k) =
O(n3)
動的計画法では問題の構造によって
O(n3), O(n2),
O(n),
O(nm) 等さまざまな計算量
動的計画法の概要 (再確認)
- (最適) 解の構造を調査、明記
- (最適) 解の値を再帰的に定義
- ボトムアップで (最適) 解の値を算出
- 計算した情報から (最適) 解を構築
動的計画法の基本要素
- 部分構造の最適性 (optimal substructure)
全体の (最適) 解が部分問題の (最適) 解から構築可能
- 部分問題の重複 (overlapping subproblems,
分割統治法との相違点)
- 履歴管理 (memoization)
Ruby による履歴管理
- 関数の変更:
- 引数をキーに結果をハッシュ等に記録
- 実際の計算の前に記録を確認、使用
- この技法は memoize と言う
- Ruby ではメタプログラミングによって実装可能
- 簡単な応用例: Cfibonacci.rb
まとめ
- 動的計画法はアルゴリズムの設計方針の一つ
- 全体の解が部分の解から得られるが、部分が重複する問題に最適
- 計算量は問題の構造に依存
宿題