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
3737flowchart
@@ -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
1031013、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$ を
1251281 行目を考えます。
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]=
187194\end{ dcases }
188195$$
189196
190- #### プログラムを書く
197+ #### プログラムを作る
191198
192199まず、表を次のように初期化します。
193200
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