Skip to content

Commit d04a549

Browse files
committed
Update dp article
1 parent 0a92ae9 commit d04a549

File tree

1 file changed

+41
-32
lines changed

1 file changed

+41
-32
lines changed

docs/02algorithms/11dp/index.mdx

Lines changed: 41 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,15 @@ $$
2323
\end{align*}
2424
$$
2525

26-
### 再帰を使った場合
26+
### 再帰を使ったフィボナッチ数列
2727

2828
再帰を使ったプログラムは次のように作れました。
2929

3030
<ViewSource path="/recursion/fib.ipynb" />
3131

32-
しかし、これでは計算量が $O(2^n)$ なので `n` が小さければこれで問題ありませんが、`n` が大きくなると時間がかかりすぎます。このプログラムで `n = 40` とすると、実行するのに 1 分もかかりました。
32+
しかし、これでは計算量が $O(2^n)$ なので `n` が小さければこれで問題ありませんが、`n` が大きくなると時間がかかりすぎます。このプログラムで `n = 40` としたら、実行するのに 1 分もかかりました。
3333

34-
原因は、次の図をみるとよくわかります。同じ数字を何度も計算してしまっています。たとえば、`fib(2)` は 3 回も計算しています。`n` が大きくなると大変です
34+
原因は、次の図をみるとよくわかります。同じ数字を何度も計算してしまっています。たとえば、`fib(2)` は 3 回も計算しています。`n` がさらに大きくなると大変です
3535

3636
```mermaid
3737
flowchart
@@ -51,7 +51,7 @@ flowchart
5151
H --> O["fib(0)"]
5252
```
5353

54-
### DP
54+
### DP を使ったフィボナッチ数列
5555

5656
これを解決するのが、動的計画法(Dynamic Programming)です。DP とよく言われます。
5757

@@ -61,19 +61,19 @@ DP には大きく分けて、二種類あります。トップダウン方式
6161

6262
#### トップダウン方式
6363

64-
計算結果をメモ化して、行うのがトップダウン方式です。メモ化再帰とも呼ばれます。
64+
計算結果をメモ化して無駄な計算を省くのがトップダウン方式です。メモ化再帰とも呼ばれます。
6565

66-
配列に計算結果をメモしておいて、すでに計算してあったらその値を利用します
66+
配列に計算結果をメモしておいて、すでに計算してあったらその値を再利用します
6767

68-
メモ化したプログラムは次のようになります。これなら、計算量は $O(n)$ なので、`n = 40` どころか `n = 100` でも余裕です
68+
メモ化してフィボナッチ数列を解くプログラムは次のようになります。これなら、計算量は $O(n)$ なので、`n = 40` どころか `n = 100` でも余裕で計算できます
6969

7070
<ViewSource path="/dp/fib_memoization.ipynb" />
7171

72-
冒頭の `memo = [-1 for _ in range(10000)]` は、リストの内包表記と呼ばれ、配列 `memo``-1` で埋める操作です。`[-1, -1, ..., -1]` となっています。
72+
冒頭の `memo = [-1 for _ in range(10000)]` は、リストの内包表記と呼ばれ、配列 `memo``-1` で埋める操作です。この操作で `memo``[-1, -1, ..., -1]` となっています。
7373

7474
#### ボトムアップ方式
7575

76-
ボトムアップ方式は、`f(2)` を求めてから `f(3)` を求めるというように下から順番に求めていこうというものです
76+
ボトムアップ方式は、`f(2)` を求めてから `f(3)` を求めて `f(3)` を求める…というように下から順番に求めていこうというものです
7777

7878
プログラムは次のようになります。こちらも、計算量は $O(n)$ です。
7979

@@ -86,32 +86,35 @@ DP には大きく分けて、二種類あります。トップダウン方式
8686
> **部分和問題**(ぶぶんわもんだい)は、計算複雑性理論・暗号理論における問題で、与えられた $n$ 個の整数 $a_1,\dots,a_n$ から部分集合をうまく選んで、その集合内の数の和が与えられた数 $N$ に等しくなるようにできるかどうかを判定する問題である。NP 完全であることが知られている。
8787
> -- <cite>[フリー百科事典『ウィキペディア(Wikipedia)』](https://ja.wikipedia.org/wiki/部分和問題)</cite>
8888
89-
例としては、3、4、6 を使って 10 を作れるかという問題であれば、4 と 6 を足せば、10 であるのでできるという答えになります
89+
例としては、3、4、6 を使って 10 を作れるかという問題であれば、4 と 6 を足せば、10 であるので**できる**という答えになります
9090

91-
### 全探索
91+
### 全探索を使う
9292

93-
この問題は、全探索すれば解くことは可能です。$a_i(1\leq n)$ を含めるか含めないかの 2 通りずつがあるので、計算量は、$O(2^n)$ です。
93+
この問題は、全探索すれば解くことは可能です。$a_i(1\leq n)$ を含めるか含めないかの 2 通りずつがあるので、計算量は、$O(2^n)$ です。説明が長くなるので、全探索のプログラムは飛ばします。
9494

95-
### 動的計画法
95+
### 動的計画法を使う
9696

97-
#### 表を考える
97+
動的計画法を使って、部分和問題を解いていきましょう。
9898

99-
表を用意します。
100-
101-
$i$ 行 $j$ 列は、$\{a_k\}(1\leq k\leq i)$ の中からいくつかを使って $j$ をつくることができるかの真偽値とします。(真は 1、偽は 0 とします)
99+
#### DP テーブルを作る
102100

103101
3、4、6 を使って 10 を作れるかという問題を考えます。
104102

103+
動的計画法は、表を作って考えるので次のように表を用意します。
104+
105105
| | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
106106
| --------------------------- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
107107
| $\varnothing$ | | | | | | | | | | | |
108108
| $\{a_1\}=\{3\}$ | | | | | | | | | | | |
109109
| $\{a_1,a_2\}=\{3,4\}$ | | | | | | | | | | | |
110110
| $\{a_1,a_2,a_3\}=\{3,4,6\}$ | | | | | | | | | | | |
111111

112-
0 行目を考えます。
112+
表の $i$ 行 $j$ 列は、$\{a_k\}(1\leq k\leq i)$ の中からいくつかを使って $j$ をつくることができるかの真偽値とします。(真は 1、偽は 0 とします)
113+
114+
まず 0 行目を考えます。
113115

114116
$0$ 行 $j$ 列は、$\varnothing$ の中からいくつかを使って $j$ をつくることができるかの真偽値となります。
117+
0 しか作れません。
115118

116119
よって、次のようになります。
117120

@@ -125,6 +128,7 @@ $0$ 行 $j$ 列は、$\varnothing$ の中からいくつかを使って $j$ を
125128
1 行目を考えます。
126129

127130
$1$ 行 $j$ 列は、$\{a_1\}=\{3\}$ の中からいくつかを使って $j$ をつくることができるかの真偽値となります。
131+
0 と 3 なら作れます。
128132

129133
よって、次のようになります。
130134

@@ -138,6 +142,7 @@ $1$ 行 $j$ 列は、$\{a_1\}=\{3\}$ の中からいくつかを使って $j$
138142
次に、2 行目を考えます。
139143

140144
$2$ 行 $j$ 列は、$\{a_1,a_2\}=\{3,4\}$ の中からいくつかを使って $j$ をつくることができるかの真偽値となります。
145+
0 と 3、4、7 が作れます。
141146

142147
よって、次のようになります。
143148

@@ -151,6 +156,7 @@ $2$ 行 $j$ 列は、$\{a_1,a_2\}=\{3,4\}$ の中からいくつかを使って
151156
次に、3 行目を考えます。
152157

153158
$3$ 行 $j$ 列は、$\{a_1,a_2,a_3\}=\{3,4,6\}$ の中からいくつかを使って $j$ をつくることができるかの真偽値となります。
159+
0 と 3、4、6、7、9、10 が作れます。
154160

155161
よって、次のようになります。
156162

@@ -161,23 +167,24 @@ $3$ 行 $j$ 列は、$\{a_1,a_2,a_3\}=\{3,4,6\}$ の中からいくつかを使
161167
| $\{a_1,a_2\}=\{3,4\}$ | 1 | 0 | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 0 | 0 |
162168
| $\{a_1,a_2,a_3\}=\{3,4,6\}$ | 1 | 0 | 0 | 1 | 1 | 0 | 1 | 1 | 0 | 1 | 1 |
163169

164-
#### 漸化式を考える
170+
#### 漸化式を作る
165171

166-
これを元に漸化式を作っていきます
172+
表を元に漸化式を作っていきます
167173

168174
$i$ 行目を考える時、$a_i$ を入れるか入れないかの二択になります。
169175

170-
- $a_i$ を入れないときは、一個上のセルの値の真偽値そのままです。つまり、$i-1$ 行 $j$ 列の真偽値となります。
171-
- $a_i$ を入れるときは、$a_k(1\leq k\leq i-1)$ を使って、**$j-a_i$** を作ることができていれば、$j$ を作れそうです。例えば、$3$ と $4$ を使って、$10-6=4$ が作れていれば、$10$ を作ることができます。
172-
そうすると、$i-1$ 行 $j-a_i$ 列の真偽値となります。
176+
- $a_i$ を入れないときは、何も変わらないので一個上のセルの値の真偽値そのままです。つまり、$i$ 行 $j$ 列の真偽値は、$i-1$ 行 $j$ 列の真偽値となります。
177+
- $a_i$ を入れるときは、$a_k(1\leq k\leq i-1)$ を使って、**$j-a_i$** を作ることができていれば、それに $a_i$ を加えることで $j$ を作れそうです。
178+
例えば、$3$ と $4$ を使って、$10-6=4$ が作れていれば、それに $6$ を加えることで $10$ を作ることができます。
179+
そうすると、$i$ 行 $j$ 列の真偽値は、$i-1$ 行 $j-a_i$ 列の真偽値となります。
173180

174-
そうすると、次の漸化式が作れます。
181+
これらをまとめると、次の漸化式が作れます。
175182

176183
$$
177184
\mathit{dp}[i][j]=dp[i-1][j]\lor dp[i-1][j-a_i]
178185
$$
179186

180-
ここで、$j-a_i<0$ のことを考えると、漸化式は次のようになります。
187+
ここで、$j-a_i<0$ のことまで考えると、漸化式は次のようになります。
181188

182189
$$
183190
\mathit{dp}[i][j]=
@@ -187,7 +194,7 @@ $$
187194
\end{dcases}
188195
$$
189196
190-
#### プログラムを書く
197+
#### プログラムを作る
191198
192199
まず、表を次のように初期化します。
193200
@@ -222,15 +229,17 @@ $$
222229
> **ナップサック問題**(ナップサックもんだい、Knapsack problem)は、計算複雑性理論における計算の難しさの議論の対象となる問題の一つで、$n$ 種類の品物(各々、価値 $v_i$、重量 $w_i$)が与えられたとき、重量の合計が $W$ を超えない範囲で品物のいくつかをナップサックに入れて、その入れた品物の価値の合計を最大化するには入れる品物の組み合わせをどのように選べばよいか」という整数計画問題である。同じ種類の品物を 1 つまでしか入れられない場合($x_i\in \{0, 1\}$)や、同じ品物をいくつでも入れてよい場合($x_i$ は 0 以上の整数)など、いくつかのバリエーションが存在する。
223230
> -- <cite>[フリー百科事典『ウィキペディア(Wikipedia)』](https://ja.wikipedia.org/wiki/ナップサック問題)</cite>
224231
225-
$x_i\in \{0,1\}$ とした、0-1 ナップサック問題を解いてみてください。
232+
ここでは、$x_i\in \{0,1\}$ とした、0-1 ナップサック問題を解いてみてください。
226233
227234
<Answer>
228235
229-
表を用意します。
236+
まずは先ほどと同様に表を考えます。
237+
238+
$v_1=2,v_2=3,v_3=6,w_1=2,w_2=3,w_3=5,W=10$ として考えてみます。
230239
231240
$i$ 行 $j$ 列は、重さが $j$ 以下になるように $i$ 番目までの品物の中からいくつかをナップサックに入れたときの価値の最大値とします。
232241
233-
$v_1=2,v_2=3,v_3=6,w_1=2,w_2=3,w_3=5,W=10$ として考えてみます
242+
表は次のようになります
234243
235244
| | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
236245
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
@@ -241,8 +250,8 @@ $v_1=2,v_2=3,v_3=6,w_1=2,w_2=3,w_3=5,W=10$ として考えてみます。
241250
242251
これから、漸化式を考えます。
243252
244-
- $i$ 番目の品物を使わなければ、一個上のセルの値そのままです。つまり、$i-1$ 行 $j$ 列の値です。
245-
- $i$ 番目の品物を使う場合は、$i-1$ 行 $j-w_i$ 列の値に $v_i$ を足したものになりそうです。
253+
- $i$ 番目の品物を使わなければ、一個上のセルの値そのままです。つまり、$i$ 行 $j$ 列の値は、$i-1$ 行 $j$ 列の値です。
254+
- $i$ 番目の品物を使う場合は、$i$ 行 $j$ 列の値は、$i-1$ 行 $j-w_i$ 列の値に $v_i$ を足したものになりそうです。
246255
247256
これから、漸化式を作ると次のようになります。
248257
@@ -254,7 +263,7 @@ dp[i][j]=
254263
\end{dcases}
255264
$$
256265
257-
プログラムは次のようになります。計算量は、$O(nW)$ です。
266+
この漸化式を使うとプログラムは次のようになります。計算量は、$O(nW)$ です。
258267
259268
<ViewSource path="/dp/knapsack.ipynb" />
260269

0 commit comments

Comments
 (0)