背景・目的
商品を販売するうえでは,生産から流通まで原材料,人件費,流通コストなど様々なコストが発生します.そのため,ただ製品を作って売れば良いというわけではありません.どの商品をどの程度製造して販売したら利益が最大になるのか分かっていないと余計な損失を生んでしまいます.
例えば,日本の農業です.日本の農業は基本的に家族経営が中心で効率的な経営が意識されることが少なく,未だにどんぶり勘定的な経営が続けられている場合が多いのが現状です(伊藤, 2006).狭い国土ゆえの生産量の向上の必要性や経営者の利益向上のためにも,趣味の園芸ではなく,他の産業と同様に効率的な経営は必要であると考えられます.
効率的な経営計画(生産や販売の戦略)を考えるには,オペレーションズリサーチ(頭文字をとってORとも呼称される)の手法を適用し,数理最適化によるシミュレーションを行うことが一つの手法として挙げられます.
そこで,本記事ではテキトーに筆者が入力したデータをもとに数理最適化について解説していきます.農業については課題を上述しましたが,街中のパン屋さんやケーキ屋さんから部品を製造するメーカーまで利益最大化は共通の課題であり,数理最適化が有効に機能する部分でもあります.
対象とするデータ
今回使用するデータは,筆者がテキトーに入力したデータです.実際のデータではないので,ところどころ明らかにおかしい点がありますが,ご容赦ください.下記がファイルへのリンクになります.
このデータは,Products(プロダクト名),Material1(使用材料1),Material2(使用材料2),Material3(使用材料3),Production_cost(製造コスト),Sales_cost(販売コスト),Sales(売上)で構成されています.
PuLPを用いた数理最適化
では,早速数理最適化をやっていきましょう.今回は,使用材料1~3にそれぞれ使用制限がある中で,製品を作り,利益(売上ー製造コストー販売コスト)を最大化する各製品の製造個数を求めていきます.今回は,材料1~3にそれぞれ制約条件を設けています.式で表すと以下のような形になります.下記の式でxが各製品の製造個数になります.
$$ Maximize \space \Sigma_{i=1}^{10}x_i(Sales_i - Production \space cost_i - Sales \space cost_i)$$
$$ \begin{align} subject \space to \space \Sigma_{i=1}^{10} x_iM1_i &\leq 300 \\ \Sigma_{i=1}^{10} x_iM2_i &\leq 100 \\ \Sigma_{i=1}^{10} x_iM3_i &\leq 150 \end{align} $$
この計算は,PythonのPuLPライブラリを用いて行います.まず,モデルを定義します.PuLPライブラリでは最大化と最小化の2種類の最適化計算が行えます.なお,今回は対象としていない多目的最適化はPuLPライブラリだと対応していません.もし,多目的最適化をやりたい場合はpymooなどのライブラリを使うことをお勧めします.今回は,利益の最大化を計算したいので,sense=LpMaximizeとしました.
m = LpProblem('Model', sense=LpMaximize)
次に,各製品の製造個数を変数として定義します.今回は,下記のように各製品の製造個数を0以上の整数値としています.細かい設定の仕方などについては,参考文献やサイトに記載の別のサイトを参考にしてください.
x_1 = LpVariable('Num_of_P01', lowBound=0, cat=LpInteger )
x_2 = LpVariable('Num_of_P02', lowBound=0, cat=LpInteger )
x_3 = LpVariable('Num_of_P03', lowBound=0, cat=LpInteger )
x_4 = LpVariable('Num_of_P04', lowBound=0, cat=LpInteger )
x_5 = LpVariable('Num_of_P05', lowBound=0, cat=LpInteger )
x_6 = LpVariable('Num_of_P06', lowBound=0, cat=LpInteger )
x_7 = LpVariable('Num_of_P07', lowBound=0, cat=LpInteger )
x_8 = LpVariable('Num_of_P08', lowBound=0, cat=LpInteger )
x_9 = LpVariable('Num_of_P09', lowBound=0, cat=LpInteger )
x_10 = LpVariable('Num_of_P10', lowBound=0, cat=LpInteger )
最後に目的関数と制約条件を設定し,ソルバーを実行します.モデルを標準出力してあげることで自身で定義した内容を確認することができます.
# 目的関数
m += (df['Sales']['P01']-df['Production_cost']['P01']-df['Sales_cost']['P01']) * x_1 + (df['Sales']['P02']-df['Production_cost']['P02']-df['Sales_cost']['P02']) * x_2 + \
(df['Sales']['P03']-df['Production_cost']['P03']-df['Sales_cost']['P03']) * x_3 + (df['Sales']['P04']-df['Production_cost']['P04']-df['Sales_cost']['P04']) * x_4 + \
(df['Sales']['P05']-df['Production_cost']['P01']-df['Sales_cost']['P05']) * x_5 + (df['Sales']['P06']-df['Production_cost']['P06']-df['Sales_cost']['P06']) * x_6 + \
(df['Sales']['P07']-df['Production_cost']['P07']-df['Sales_cost']['P07']) * x_7 + (df['Sales']['P08']-df['Production_cost']['P08']-df['Sales_cost']['P08']) * x_8 + \
(df['Sales']['P09']-df['Production_cost']['P09']-df['Sales_cost']['P09']) * x_9 + (df['Sales']['P10']-df['Production_cost']['P10']-df['Sales_cost']['P10']) * x_10
# 制約条件
m += df['Material1']['P01'] * x_1 + df['Material1']['P02'] * x_2 + \
df['Material1']['P03'] * x_3 + df['Material1']['P04'] * x_4 + \
df['Material1']['P05'] * x_5 + df['Material1']['P06'] * x_6 + \
df['Material1']['P07'] * x_7 + df['Material1']['P08'] * x_8 + \
df['Material1']['P09'] * x_9 + df['Material1']['P10'] * x_10 <= M1
m += df['Material2']['P01'] * x_1 + df['Material2']['P02'] * x_2 + \
df['Material2']['P03'] * x_3 + df['Material2']['P04'] * x_4 + \
df['Material2']['P05'] * x_5 + df['Material2']['P06'] * x_6 + \
df['Material2']['P07'] * x_7 + df['Material2']['P08'] * x_8 + \
df['Material2']['P09'] * x_9 + df['Material2']['P10'] * x_10 <= M2
m += df['Material3']['P01'] * x_1 + df['Material3']['P02'] * x_2 + \
df['Material3']['P03'] * x_3 + df['Material3']['P04'] * x_4 + \
df['Material3']['P05'] * x_5 + df['Material3']['P06'] * x_6 + \
df['Material3']['P07'] * x_7 + df['Material3']['P08'] * x_8 + \
df['Material3']['P09'] * x_9 + df['Material3']['P10'] * x_10 <= M3
m.solve() # ソルバーの実行
print(m)
print('P01 : ', int(value(x_1)), \
'\nP02 : ', int(value(x_2)), \
'\nP03 : ', int(value(x_3)), \
'\nP04 : ', int(value(x_4)), \
'\nP05 : ', int(value(x_5)), \
'\nP06 : ', int(value(x_6)), \
'\nP07 : ', int(value(x_7)), \
'\nP08 : ', int(value(x_8)), \
'\nP09 : ', int(value(x_9)), \
'\nP10 : ', int(value(x_10)), \
'\nTotal sales : ', int(value(m.objective)))
今回のデータでは,P03を1個,P09を3個,P10を9個作るのがベストでその際の利益は60261となりました.
P01 : 0
P02 : 0
P03 : 1
P04 : 0
P05 : 0
P06 : 0
P07 : 0
P08 : 0
P09 : 3
P10 : 9
Total sales : 60261
一連の流れについては,コードをご確認いただけますと幸いです.
まとめ
本記事では筆者がテキトーに入力したデータをもとにPuLPを用いた数理最適化の方法について簡単に解説しました.今回は,データがテキトーであったため,理解が難しい部分もありました.しかし,実際のデータを入力してみると意外と自身が感じていた感覚と同じ結果が出てくることもあり,自身の販売戦略の定量的な裏付けが得られる場合があります.ちなみにですが,筆者はお手伝いしていた農園の販売戦略を練る際に,数理最適化を使うことで定量的な戦略を練ることができました.
参考文献やサイトなど
- 伊藤健. 2006. 数理最適化を利用した営農計画. 神戸学院経済学論集. Vol. 38. Issue1-2. https://kobegakuin-economics.jp/wp-content/uploads/2021/09/200609_38_021.pdf
- 株式会社ビープラウド(PyQ). PuLPによるモデル作成方法. https://docs.pyq.jp/python/math_opt/pulp.html, accessed on 10 Apr. 2024.