はなちるのマイノート

Unityをメインとした技術ブログ。自分らしくまったりやっていきたいと思いますー!

アルゴリズムイントロダクション第15章後半の個人まとめ

動的計画法の基本要素

部分構造最適性

問題の最適解が,その内部に,その部分問題に対する最適解を含む.

重みなし最短路問題

入力:有向グラフ,頂点 u, v
出力:辺数最小の uから vへの道

重みなし最短路問題が部分構造最適性を持つことの証明.(切貼り法を用いる)

問題が自明にならないように u\neq vと仮定.
 uから vへの任意の道は中間頂点 wを持つ.( w uでも vでも良い)
 u\overset{p}{\sim}> vは部分道 u\overset{p_1}{\sim}> v u\overset{p_2}{\sim}> v に分解できる.

 p_1より短い uから wへの道 p_1'が存在すると仮定.
 p_1 p_1'に置き換えると pよりも短い道ができるので矛盾.
同様に p_2 wから vへの最短路.

重みなし最長単純道問題

入力:有向グラフ,頂点 u, v
出力:辺数最大の uから vへの単純道

重みなし最長単純道問題が部分構造最適性を持たないことの証明.

最長単純道 u\overset{p}{\sim}> vは部分道 u\overset{p_1}{\sim}> v u\overset{p_2}{\sim}> v に分解できない反例を示す.

f:id:hanaaaaaachiru:20211206213823p:plain
反例

上図の 𝑞→𝑟→𝑡 𝑞から 𝑡への最長単純道.
しかし 𝑞→𝑟 𝑞から 𝑟への最長単純道でない.

部分問題重複性

問題に対する再帰アルゴリズムの部分問題の空間が“小さい”こと.
アルゴリズムが常に新しい部分問題を生成するのではなく,同じ部分問題を繰り返し解く場合.

最長共通部分列問題

部分列

列Zと列Xについて,Xから0個以上の文字を取り除いたあと,残りの文字を元の順序で連結することでZが得られるとき,ZはXの部分列であるという.
例. X = 〈A, B, C, B, D, A, B〉,Z = 〈B, C, D, B〉

共通部分列

列Xと列Yと列Zについて,ZがXとYの両方の部分列であるとき,ZをXとYの共通部分列という.
例. X = 〈A, B, C, B, D, A, B〉,Y = 〈B, D, C, A, B, A〉,Z = 〈B, C, B, A〉

最長共通部分列問題

入力:二つの列 𝑋 = <𝑥_1, 𝑥_2,…,𝑥_𝑚> Y = <𝑦_1, 𝑦_2,…,𝑦_𝑚>
出力:XとYの最長共通部分列

最長共通部分列問題はlongest-common-subsequence problemと英語で表記し,LCS問題と呼ばれることが多い.

LCS問題をシラミツブシ法で解いてみる.
与えられた二つの列 𝑋 = <𝑥_1, 𝑥_2,…,𝑥_𝑚> Y = <𝑦_1, 𝑦_2,…,𝑦_𝑚>に対して,Xの部分列を全て列挙(2^𝑚個ある)し,Yの部分列か調べ,最長の共通部分列を記録する.
Xの部分列の個数から指数時間かかることが分かる.

接頭語

任意の列 𝑋 = <𝑥_1, 𝑥_2,…,𝑥_𝑚>について,Xの 𝑖 番目の接頭語(prefix) 𝑋 = <𝑥_1, 𝑥_2,…,𝑥_𝑖>と定義.
例.  𝑋 = <𝐴, 𝐵, 𝐶, 𝐵, 𝐷, 𝐴, 𝐵> 𝑋_4  = <𝐴, 𝐵, 𝐶, 𝐵> 𝑋_0は空列

LCSの部分構造最適性

 𝑋 = 〈𝑥_1, 𝑥_2,…,𝑥_𝑚〉 Y = 〈𝑦_1, 𝑦_2,…,𝑦_n〉を列, Z = 〈z_1, z_2,…,z_k〉 X Yの任意のLCSとする.

  1.  x_m = y_nならば, z_k = x_m = y_nであり, Z_{k-1} X_{m-1} Y_{n-1}のLCSである.
  2.  x_m \neq y_nのとき, z_k \neq x_mならば, Z X_{m-1} YのLCSである.
  3.  x_m \neq y_nのとき, z_k \neq y_nならば, Z X Y_{n-1}のLCSである.

1の前半の証明.
 z_k \neq x_m = y_nと仮定. Zの末尾に x_mを追加した列もXとYの共通部分列で長さがk+1なのでZより長い列.
これはZがLCSであることに矛盾.

1の後半の証明.
 Z_{k-1} X_{m-1} Y_{n-1}の長さがk-1の共通部分列.これがLCS(最長)であることを示す.
長さがk以上の X_{m-1} Y_{n-1}の共通部分列Wが存在すると仮定.
Wに x_m = y_nを付与すると,長さがk+1以上のXとYの共通部分列ができて矛盾.

2の証明.
 z_k  \neq x_mならばZは X_{m-1}とYの共通部分列.これがLCS(最長)であることを示す.
長さがk+1以上の X_{m-1}とYの共通部分列Wが存在すると仮定.
Wは X_mとYの共通部分列でもあるので,ZがXとYのLCSである仮定に矛盾.

3の証明.
2と同様.

LCS問題の定式化

 X_i Y_jのLCSの長さをc[i, j]と定義.
LCS問題の部分構造最適性から以下の漸化式を得る.

 c [ i,j ]  = \begin{cases} 0 & if i=0\vee j=0 \\ c[ i-1, j-1 ]+1 & if i,j>0 \wedge x_i = y_j \\ max(c [ i, j-1 ] , c [ i-1,j ] ) & if i,j>0 \wedge x_i \neq y_j \end{cases}

LCS問題のアルゴリズム

f:id:hanaaaaaachiru:20211206222346p:plain
プログラム

b[i,j] : LCSの再構成の用いる表
c[i,j] :  X_i Y_jのLCSの長さ

計算量は \Theta(m,n)

最適2分探索木

最適2分探索木

ソート済みのn個のキー 𝐾=<𝑘_1,𝑘_2,…,𝑘_𝑛>(𝑘_1<𝑘_2<…<𝑘_𝑛)と各キー 𝑘_𝑖を探索する確率 𝑝_𝑖,Kが含まない値を示す「ダミーキー」𝑑_0, 𝑑_1, …,𝑑_𝑛と探索が 𝑑_𝑖で終わる確率 𝑞_𝑖が与えられたとき,探索コストの期待値を最小化する2分探索木.

f:id:hanaaaaaachiru:20211206223043p:plain
以下のpi,qi,n=5のときの最適2分探索木
f:id:hanaaaaaachiru:20211206223220p:plain
出現確率
ダミーキー

 𝑑_0 𝑘_1未満, 𝑑_𝑛 𝑘_𝑛以上, 𝑑_𝑖 𝑘_𝑖 𝑘_{𝑖+1}の間の全ての値を表現する.

期待値の計算

一回の探索にかかる実コストを訪れた節点数,すなわち発見された節点のTにおける深さ+1と仮定.

一回の探索コストの期待値は以下の通り.
 E [search cost in T ] = \sum_{i=1}^n(depth_T(k_i) + 1)\cdot p_i + \sum_{i=0}^n(depth_T(d_i) + 1)\cdot q_i
 E [search cost in T ] = 1+ \sum_{i=1}^ndepth_T(k_i)\cdot p_i + \sum_{i=0}^ndepth_T(d_i)\cdot q_i

 depth_Tは木 Tにおける節点の深さ

シラミツブシ法

n個の節点を持つ任意の2分木の節点をキー 𝑘_1,𝑘_2,…,𝑘_𝑛でラベル付け.ダミーキーを追加.
n個の節点を持つ2分木の個数は Ω(4^𝑛/𝑛^(3/2))

シラミツブシでは指数時間かかる.

部分構造最適性

最適2分探索木を求める問題が部分構造最適性を持つことを証明する.

最適2分探索木𝑇が部分木𝑇’を持つとすると,𝑇’よりも期待コストの小さい𝑇’’があるなら𝑇から𝑇’を切り取り代わりに𝑇’’を貼り込むことで, 𝑇よりも期待コストが小さくなり矛盾.
よって,最適2分探索木𝑇の部分木𝑇’も最適2分探索木.

最適解の構成

最適部分木の根はキー 𝑘_𝑟 (𝑖≤𝑟≤𝑗)を根に持ち、これに対し左部分木と右部分木はそれぞれキー 𝑘_𝑖…𝑘_{𝑟−1} 𝑘_{𝑟+1}…𝑘_𝑗を持つ.
全ての根の候補に対して左部分木と右部分木のそれぞれの最適2分探索木を決定することで問題の最適2分探索木を必ず発見できる.

 e [ i, j ] を以下のように定義&漸化式が成り立つ.
 e [ i,j ] := キーk_i,..,k_jを含む最適2分探索木の探索コストの期待値


 e [ i,j]  := \begin{cases}q_{i-1} & if j=i-1\\ \underset{i \leq r \leq j}{min} \{ e[ i, r-1]  + e[ r+1, j] + w(i,j) \} & if i \leq j \end{cases}

アルゴリズム

f:id:hanaaaaaachiru:20211206225311p:plain
疑似コード

計算量 \Theta(n^3)