🤖

【Python】ナップサック問題

2023/08/11に公開

作るもの

再帰を使用してナップサック問題を解決する関数。内部関数 dfs は、現在のアイテムを選ぶか選ばないかを選択肢としながら、可能な組み合わせを試すことで最適解を探索する。関数 knapsackProblem は、最初に dfs を初期値で呼び出してナップサック問題を解決し、最終的な結果を返す。

※ ナップサック問題(Knapsack Problem)とは?
最適化問題の一つであり、与えられた一定の容量を持つナップサック(背負い袋)に、異なるアイテムの集合を入れていくことで、アイテムの価値の合計を最大化する問題。

実装

knapsack_problem.py
from typing import List, Tuple

def knapsackProblem(
    items: List[Tuple[int, int]], capacity: int
) -> Tuple[int, List[int]]:
    # ナップサック問題を再帰的に解く関数
    def dfs(
        i: int, capacity: int, currValue: int, indices: List[int]
    ) -> Tuple[int, List[int]]:
        # ベースケース: アイテムのインデックスが最大値を超えたり、容量が0以下の場合
        if i >= len(items) or capacity <= 0:
            return currValue, indices  # 現在の価値と選ばれたアイテムの組み合わせを返す

        # 現在のアイテムの価値と重さを取得
        value = items[i][0]
        weight = items[i][1]

        # 現在のアイテムを選ばない場合の再帰呼び出し
        exclude = dfs(i + 1, capacity, currValue, indices)

        # 現在のアイテムを選ぶ場合の条件判定
        if capacity - weight >= 0:
            # 現在のアイテムを選んだ場合の再帰呼び出し
            include = dfs(i + 1, capacity - weight, currValue + value, indices + [i])

            # 選ぶ場合と選ばない場合の価値を比較し、高い方を選ぶ
            if include[0] >= exclude[0]:
                return include[0], include[1]
            return exclude[0], exclude[1]
        else:
            # 容量が足りない場合は、アイテムを選ばないで次のステップへ進む
            return exclude[0], exclude[1]

    # dfs関数を初期値で呼び出して、ナップサック問題を解く
    return dfs(0, capacity, 0, [])

# テスト
if __name__ == "__main__":
    capacity = 12
    items = [[1, 2], [4, 3], [5, 6], [6, 7]]
    print(knapsackProblem(items, capacity))

出力
# (最大価値, [選択するもののindex番号のリスト])。この場合、[1, 2]と[4, 3]と[6, 7]を選択する。
(11, [0, 1, 3])

補足

currValue: 現在の価値の合計
indices: 選ばれたアイテムのインデックスのリスト

参考

https://www.algoexpert.io

Discussion