Python ortoolpyで最適化問題を解く

個人開発したアプリの宣伝
目的地が設定できる手帳のような使い心地のTODOアプリを公開しています。
Todo with Location

Todo with Location

  • Yoshiko Ichikawa
  • Productivity
  • Free

スポンサードリンク

"Python実践データ分析100本ノック"、この本を進めていて少し理解に時間がかかったところをメモしておきます。


最適化問題とは?

条件を満たす解の中で一番よいものを求める問題。みたいなこと。

書籍では、倉庫から工場の商品1つあたりの輸送コスト、df_tcがあり、

f:id:letitride:20200715163228p:plain:w200

Fが工場、Wが倉庫を表す。

各工場の需要量、各倉庫の在庫数を制限とし、総輸送コストが最も低くなる最適化問題を紹介していた。


ortoolpy, pulpのインストール

anacondaには数理モデルライブラリが入ってないようなので、pipでインストールします。

$ pip3 install pulp
$ pip3 install ortoolpy


数理モデルを使用して解を求める

import numpy as np
import pandas as pd
from itertools import product
from pulp import LpVariable, lpSum, value
from ortoolpy import model_min, addvars, addvals

# 数理モデル作成 最小化するモデル
m1 = model_min()

# 解決する変数の定義 (3行 * 4列の座標位置(配送ルート)に対する配送量を格納するdictionary)
for i in range(3):
    for j in range(4):
        v1 = {(i,j):LpVariable("v%d_%d"%(i,j),lowBound=0)}


# 目的関数の定義 輸送ルートコストと(変数:倉庫から工場への配送量)の積の和(総コスト)が最小となるモデル。ここでは変数v1、最小のコストになる各倉庫から工場に対しての輸送量を求める
m1 += lpSum(df_tc.iloc[i][j]*v1[i,j] for i,j in pr)
# 制約条件 結果の数量は各制約を満たす数量でなければならない
for i in range(3):
    m1 += lpSum(v1[i,j] for j in range(4)) <= 在庫表.iloc[0][i]
for j in range(4):
    m1 += lpSum(v1[i,j] for i in range(3)) >=需要表.iloc[0][j]
# solveでこの変数v1が解決される
m1.solve()

#倉庫から工場への輸送量を表現するのDataFrame
df_tr_sol = df_tc.copy()
total_cost = 0
# 結果はitems()で取得できる kに変数名の引数(この場合、座標)、xに計算された数量が入る
for k,x in v1.items():
    i,j = k[0],k[1]
    df_tr_sol.iloc[i][j] = value(x)
    total_cost += df_tc.iloc[i][j]*value(x)


この部分が理解するのに時間がかかった(^_^;) 各コストに対しての数量を座標に応じた空変数として用意し、solveで数量を解決する。といったイメージだと思う。

for i in range(3):
    for j in range(4):
        v1 = {(i,j):LpVariable("v%d_%d"%(i,j),lowBound=0)}

m1 += lpSum(df_tc.iloc[i][j]*v1[i,j] for i,j in pr)


理解の参考になったページ: チーム内の業務割当てをPython + PuLPで最適化 - 昭和生まれ30代経理マンが令和に始めたBlog