(Gスプレッドシート)ナップザック問題を関数で解く(総当たり法)

 Googleスプレッドシートで関数によりナップザック問題を解く例についてです。
 ただし1セルでの解決にこだわったせいで長い式になっています。半分ネタと思ってご覧ください。

  • ナップザック問題のような問題も関数で扱うことができます。

手順

f:id:accs2014:20190616110956p:plain:right:w650

 B,C列にアイテムの価値と重量の組み合わせが記録されています。また、C2にはアイテム数が、C3には上限重量が記録されています。
 上限重量を超えない範囲で価値が最大になるようなアイテムの組み合わせを選ぶものとします。

 F4セルに次のように入力します。
 

=TRANSPOSE(QUERY(MMULT(ARRAYFORMULA(SPLIT(REGEXREPLACE(BASE(ROW(INDIRECT("1:"&2^B3-1)),2,B3),"","_"),"_")),{OFFSET(B6,0,0,B3,2),MUNIT(B3)}),"SELECT * WHERE Col2<="&C3&" ORDER BY Col1 DESC,Col2"))

ROW関数で0,0,0,…,1から1,1,1,…,1までの組み合わせを生成し、価値列(B)+重量列(C)+単位行列との掛け算により総価値・総重量と選択の組み合わせ(結果表示用)を得ます。次にQUERY関数により重量オーバーとなる組み合わせの削除と並べ替えを行い、最後にTRANSPOSE関数で表にフィットするよう転置します。
 

f:id:accs2014:20190616110953p:plain:right:w650

 実行結果です。
 1,4,7番目のアイテムを選択することで(F6,F9,F12の値が1)価値が385となり(F4の値)、このときの重量は97となります(F5の値)。これが最も価値の高い組み合わせで、あとは価値が高い順に答えが右に並んでいきます。同価値の組み合わせが複数ある場合、総重量の軽い方が左に来ます。
 なお、B6以降に記録されているアイテム数がB3の数値より少ないとエラーになります。逆に多いと下の方のアイテムが結果に反映されません。
 総当たりのためアイテムが増えると急激に重くなります。また、すべての組み合わせを結果を表示する列数が足りなくなります。表示に支障が出る場合はQUERY関数のLIMIT句などで調整してください。