0-1ナップサック問題をPythonで解く方法:全探索と動的計画法の実装
この記事では、以下の問題文に対する解決策について学びます。
問題文 − n個のアイテムの重さと価値が与えられます。これらのアイテムを容量Wのバッグに詰め込み、バッグの最大容量を超えない範囲で、合計価値が最大になる組み合わせを見つけて返す必要があります。
それでは、以下の実装で解決策を確認してみましょう。
# 全探索(ブルートフォース)アプローチ
まずは再帰を使った素朴な全探索から見ていきます。各アイテムについて「入れる」か「入れない」かをすべて試し、得られる価値の最大値を求めます。
例
# バッグに格納できる最大価値を返す関数
def knapSack(W, wt, val, n):
# 初期条件
if n == 0 or W == 0:
return 0
# アイテムの重さが残り容量を超える場合は含めない
if (wt[n-1] > W):
return knapSack(W, wt, val, n-1)
# n番目のアイテムを「含める場合」と「含めない場合」の大きい方を返す
else:
return max(val[n-1] + knapSack(W-wt[n-1], wt, val, n-1),
knapSack(W, wt, val, n-1))
# 上記の関数をテストする
val = [50,100,150,200]
wt = [8,16,32,40]
W = 64
n = len(val)
print(knapSack(W, wt, val, n))
出力
350
この全探索アプローチの計算量はO(2n)となり、アイテム数が増えると実行時間が指数関数的に増大するという課題があります。
# 動的計画法アプローチ
次に、動的計画法(DP)を用いた効率的な解法です。部分問題の結果を表(2次元配列)に記録しながらボトムアップ方式で計算することで、同じ計算の重複を避けられます。
例
# 動的計画法によるアプローチ
# バッグに格納できる最大価値を返す関数
def knapSack(W, wt, val, n):
K = [[0 for x in range(W + 1)] for x in range(n + 1)]
# ボトムアップ方式で表を作成
for i in range(n + 1):
for w in range(W + 1):
if i == 0 or w == 0:
K[i][w] = 0
elif wt[i-1] <= w:
K[i][w] = max(val[i-1] + K[i-1][w-wt[i-1]], K[i-1][w])
else:
K[i][w] = K[i-1][w]
return K[n][W]
# メイン処理
val = [50,100,150,200]
wt = [8,16,32,40]
W = 64
n = len(val)
print(knapSack(W, wt, val, n))
出力
350
すべての変数はローカルスコープ内で宣言されており、その参照関係は上図の通りです。
動的計画法では、計算量がO(n×W)に抑えられるため、全探索と比べて大幅に高速化できます。ただし、その分O(n×W)サイズのメモリが必要になる点には注意しましょう。
まとめ
この記事では、0-1ナップサック問題を解くためのPythonプログラムとして、再帰による全探索アプローチと動的計画法アプローチの2種類を実装しました。小規模な入力なら全探索でも十分ですが、実用的な規模の問題では動的計画法が有効です。
-
Pythonで単利を計算するプログラムの作成方法
この記事では、Python 3.x(およびそれ以前のバージョン)を使用して、単利を計算する方法について解説します。単利とは、元本に対して一定の利率で発生する利息のことです。一般的には、利率に元本を掛け、さらに利息が発生する期間を掛けることで求められます。単利の計算式単利は数学的に以下の式で表すことができます。単利(SI) = (P × T × R) / 100 P:元本(Principal) T:期間(Time) R:利率(Rate)例えば、元本 P = 1000、利率 R = 1%、期間 T = 2 の場合、単利は次のように計算されます。SI = (1000 × 1 × 2) / 100
-
Pythonで選択ソートを実装する方法|仕組みとサンプルコードをわかりやすく解説
この記事では、選択ソート(Selection Sort)の基本的な仕組みと、Python 3.x(およびそれ以前のバージョン)での実装方法について解説します。 選択ソートとは 選択ソートは、ソートされていない部分から最小の要素を繰り返し見つけ出し、先頭側へ移動させることで配列全体を整列していくアルゴリズムです。処理の過程で、対象の配列は次の2つの部分配列に分けられます。 すでにソートが完了している部分配列 まだソートされていない部分配列 選択ソートの各イテレーションでは、未ソートの部分配列から最小要素を取り出し、ソート済みの部分配列の末尾に追加していきます。 アルゴリズムの動作イメー