0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ナップサック問題を解くPythonプログラムの実装例

Posted at

はじめに

こんにちは!今回はナップサック問題(Knapsack Problem)をPythonで解くプログラムを紹介します。このプログラムは、動的計画法(Dynamic Programming)を用いて「制限された重量の中で、価値の最大化」を達成するアイテムの組み合わせを求めます。さらに、結果を見やすく表示するためにpandasを活用しています。

ナップサックとは?

  • ナップサック問題は次のような最適化問題
  • 複数のアイテムであり、それぞれに「重さ」と「価値」が割り当て
  • ナップサックの容量が決まっており、その容量を超えないようにアイテムを選択
  • 選択したアイテムの総価値を最大化することが目的

実装コード

以下がプログラムの全体コードです。

from decimal import Decimal
import pandas as pd


def knapsack(goods, weights):
    """
    ナップサック問題を動的計画法で解く関数

    Args:
        goods: 各アイテムの名前、重さ、価値を含む辞書
        weights: ナップサックの容量リスト

    Returns:
        動的計画法のテーブルを返す
    """
    n = len(goods)
    table = [[(Decimal(0), [])] * (len(weights)+1) for _ in range(n+1)]
    for i, item in enumerate(goods, 1):
        for j in range(1, len(weights)+1):
            w = goods[item][0]
            w_id = weights.index(w) + 1

            if weights[j-1] < w:
                table[i][j] = table[i-1][j]
            else:
                tmp1 = table[i-1][j][0]
                tmp2 = table[i-1][j-w_id][0] + goods[item][1]
                if tmp1 >= tmp2:
                    table[i][j] = table[i-1][j]
                else:
                    table[i][j] = (tmp2, table[i-1][j-w_id][1] + [item])
    return table


def format_list(data, index_=None, columns_=None):
    """
    動的計画法のテーブルを整形して DataFrame に変換する関数

    Args:
        data: 動的計画法のテーブル
        index_: 行ラベル
        columns_: 列ラベル

    Returns:
        整形された DataFrame
    """
    data = [[(int(value), _) for value, _ in inside] for inside in data]  # Decimalをintに変換

    formatted_list = []
    for sublist in data[1:]:
        tmp = []
        for value, names in sublist[1:]:
            tmp.append(f"({value}, {{{', '.join(names if names else '𝜙')}}})")
        formatted_list.append(tmp)
    return pd.DataFrame(formatted_list, index=index_, columns=columns_)


# 実行例
GOODS = {  # アイテム名: (重さ, 価値)
    'A': (2.5, 200),
    'B': (1, 100),
    'C': (2, 50),
    'D': (0.5, 150),
    'E': (1.5, 200),
}
weights = [Decimal(str(0.5 * i)) for i in range(1, 7)]  # 重さのリスト: [0.5, 1.0, ..., 3.0]

result = knapsack(GOODS, weights)
display(format_list(result, GOODS.keys(), weights))

プログラムの説明

動的計画法を使った解法

動的計画法の考え方を利用して、ナップサック問題を解きます。「価値が最大となる組み合わせ」を表形式で記録しながら計算します。

  • knapasck関数は、各重さごとの最大価値を計算し、同時にそのときのアイテムの組み合わせも保存
  • format_list関数は計算結果を整形し、DateFrameとして表示

入力データ

  • GOODS:各アイテムの重さと価値を表す辞書
  • weights:ナップサックに入れられる重さのリスト

実行結果

以下のこのプログラムの実行結果です。

       0.5        1.0       1.5       2.0       2.5       3.0
A  (0, {𝜙})  (0, {𝜙})  (0, {𝜙})  (0, {𝜙})  (200, {A}) (200, {A})
B  (0, {𝜙}) (100, {B}) (100, {B}) (100, {B}) (200, {A}) (200, {A})
C  (0, {𝜙}) (100, {B}) (100, {B}) (100, {B}) (200, {A}) (200, {A})
D  (150, {D}) (150, {D}) (250, {D, B}) (250, {D, B}) (350, {D, A}) (350, {D, A})
E  (150, {D}) (150, {D}) (200, {E}) (350, {D, E}) (350, {D, A}) (450, {D, A, E})

まとめ

ナップサック問題をPythonで解く方法を紹介しました。動的計画法を利用すれば、複雑な最適化問題も効率的に解けます。最後まで読んでくださり、ありがとうございました。もし改善点や質問があれば、ぜひコメントしてください!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?