1
2

More than 1 year has passed since last update.

指定の金額に対してどの硬貨を何枚出すのかを、LP問題として、PuLPを用いて求めてみる

Last updated at Posted at 2021-09-30

指定の金額に対してどの硬貨を何枚出すのか(小学生用の計算ドリルに載っていそうな問題)を、LP問題として、PythonライブラリPuLPを用いて求めてみました。

PuLP

PuLP: Pythonで線形最適化を行うライブラリ。
使用方法は、以下が詳しい。
https://docs.pyq.jp/python/math_opt/pulp.html

導入・バージョン確認・import

インストールは、Windowsのコマンドプロンプトを用いて、DOSコマンドでインストールする場合、

python.exe -m pip install pulp

Jupyter内でインストールする場合は、

pip install pulp

確認すると、

pip show pulp
Name: PuLP
Version: 2.3
Summary: PuLP is an LP modeler written in python. PuLP can generate MPS or LP files and call GLPK, COIN CLP/CBC, CPLEX, and GUROBI to solve linear problems.
Home-page: https://github.com/coin-or/pulp
Author: J.S. Roy and S.A. Mitchell
Author-email: pulp@stuartmitchell.com
License: UNKNOWN
Location: D:\wpy64-3950\python-3.9.5.amd64\lib\site-packages
Requires: amply
Required-by: 
Note: you may need to restart the kernel to use updated packages.

インポートは、

import pulp

例題を用意

指定の金額に応じて、どの硬貨を何枚出すのかを求める問題を考える。1円玉が5枚、5円玉が4枚、10円玉が3枚、50円玉が2枚あるとして、金額、例えば73円を出す場合、

  • 出す硬貨の枚数を最小にする場合 (→最も硬貨を出す手作業を軽減する場合)
  • 出す硬貨の枚数を最大にする場合 (→最も財布の中の硬貨を減らす場合)

において、どの硬貨を何枚出すのか、以降で求める。

出す硬貨の数を最小にしたい場合

money = 73 # 金額

LP = pulp.LpProblem(sense=pulp.LpMinimize)
yen1 = pulp.LpVariable("1円玉", 0, 5, cat=pulp.LpInteger)
yen5 = pulp.LpVariable("5円玉", 0, 4, cat=pulp.LpInteger)
yen10 = pulp.LpVariable("10円玉", 0, 3, cat=pulp.LpInteger)
yen50 = pulp.LpVariable("50円玉", 0, 2, cat=pulp.LpInteger)
LP += 1*yen1 + 5*yen5 + 10*yen10 + 50*yen50 == money
LP += pulp.lpSum([yen1, yen5, yen10, yen50]) # 合計枚数最小
LP.solve()
print("求解の結果:", pulp.LpStatus[LP.status])
求解の結果: Optimal
print(f"""金額{money}円には、
  1円玉{pulp.value(yen1):.0f}枚、5円玉{pulp.value(yen5):.0f}枚、
  10円玉{pulp.value(yen10):.0f}枚、50円玉{pulp.value(yen50):.0f}""")
金額73円には、
  1円玉3枚、5円玉0枚、
  10円玉2枚、50円玉1枚

出す硬貨の数を最大にしたい場合

money = 73 # 金額

LP = pulp.LpProblem(sense=pulp.LpMaximize)
yen1 = pulp.LpVariable("1円玉", 0, 5, cat=pulp.LpInteger)
yen5 = pulp.LpVariable("5円玉", 0, 4, cat=pulp.LpInteger)
yen10 = pulp.LpVariable("10円玉", 0, 3, cat=pulp.LpInteger)
yen50 = pulp.LpVariable("50円玉", 0, 2, cat=pulp.LpInteger)
LP += 1*yen1 + 5*yen5 + 10*yen10 + 50*yen50 == money
LP += pulp.lpSum([yen1, yen5, yen10, yen50]) # 合計枚数最大
LP.solve()
print("求解の結果:", pulp.LpStatus[LP.status])
求解の結果: Optimal
print(f"""金額{money}円には、
  1円玉{pulp.value(yen1):.0f}枚、5円玉{pulp.value(yen5):.0f}枚、
  10円玉{pulp.value(yen10):.0f}枚、50円玉{pulp.value(yen50):.0f}""")
金額73円には、
  1円玉3枚、5円玉4枚、
  10円玉0枚、50円玉1枚

得られた結果

金額73円は、

  • 1円玉3枚、5円玉0枚、10円玉2枚、50円玉1枚、の最小6枚で作ることが出来、また、
  • 1円玉3枚、5円玉4枚、10円玉0枚、50円玉1枚、の最大8枚で作ることが出来る。

手持ちの硬貨の枚数を倍にした場合

1円玉が5枚、5円玉が4枚、10円玉が3枚、50円玉が2枚あったものから、
1円玉が10枚、5円玉が8枚、10円玉が6枚、50円玉が4枚に条件を変えて、再度、以降で求めてみる。

出す硬貨の数を最小にしたい場合

money = 73 # 金額

LP = pulp.LpProblem(sense=pulp.LpMinimize)
yen1 = pulp.LpVariable("1円玉", 0, 10, cat=pulp.LpInteger)
yen5 = pulp.LpVariable("5円玉", 0, 8, cat=pulp.LpInteger)
yen10 = pulp.LpVariable("10円玉", 0, 6, cat=pulp.LpInteger)
yen50 = pulp.LpVariable("50円玉", 0, 4, cat=pulp.LpInteger)
LP += 1*yen1 + 5*yen5 + 10*yen10 + 50*yen50 == money
LP += pulp.lpSum([yen1, yen5, yen10, yen50]) # 合計枚数最小
LP.solve()
print("求解の結果:", pulp.LpStatus[LP.status])
求解の結果: Optimal
print(f"""金額{money}円には、
  1円玉{pulp.value(yen1):.0f}枚、5円玉{pulp.value(yen5):.0f}枚、
  10円玉{pulp.value(yen10):.0f}枚、50円玉{pulp.value(yen50):.0f}""")
金額73円には、
  1円玉3枚、5円玉0枚、
  10円玉2枚、50円玉1枚

同じ結果となる。

出す硬貨の数を最大にしたい場合

money = 73 # 金額

LP = pulp.LpProblem(sense=pulp.LpMaximize)
yen1 = pulp.LpVariable("1円玉", 0, 10, cat=pulp.LpInteger)
yen5 = pulp.LpVariable("5円玉", 0, 8, cat=pulp.LpInteger)
yen10 = pulp.LpVariable("10円玉", 0, 6, cat=pulp.LpInteger)
yen50 = pulp.LpVariable("50円玉", 0, 4, cat=pulp.LpInteger)
LP += 1*yen1 + 5*yen5 + 10*yen10 + 50*yen50 == money
LP += pulp.lpSum([yen1, yen5, yen10, yen50]) # 合計枚数最大
LP.solve()
print("求解の結果:", pulp.LpStatus[LP.status])
求解の結果: Optimal
print(f"""金額{money}円には、
  1円玉{pulp.value(yen1):.0f}枚、5円玉{pulp.value(yen5):.0f}枚、
  10円玉{pulp.value(yen10):.0f}枚、50円玉{pulp.value(yen50):.0f}""")
金額73円には、
  1円玉8枚、5円玉7枚、
  10円玉3枚、50円玉0枚

枚数がかなり増加する。

得られた結果

(1円玉が10枚、5円玉が8枚、10円玉が6枚、50円玉が4枚の場合)、金額73円は、

  • 1円玉3枚、5円玉0枚、10円玉2枚、50円玉1枚、の最小6枚で作ることが出来、また、
  • 1円玉8枚、5円玉7枚、10円玉3枚、50円玉0枚、の最大18枚で作ることが出来る。

(小学生の頃、変数の数が数個で&条件式は1つだけですが、簡単なLP問題を、皆解いていたということか... いや違うか... 単にスケール可能な問題としてコンピュータで解く場合はこうなりますということか。)

1
2
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
1
2