LoginSignup
1
1

More than 3 years have passed since last update.

生産現場で役に立つかもしれない最適化手法2【GLPK】(工数の平準化)

Posted at

<--目次へ

はじめに

工数の平準化についてGLPKを用いたプログラムを作ってみました。GLPKのインストールや使い方は上の目次リンクからたどってください。

まずは非常に単純化した例です。

最適化の条件1

  • 製品ごとに必要な工数が与えられており、日ごとの最大総工数以内になるように生産計画を立てたい。
  • 各製品の生産は1日で終了する。
  • 製品の製造できる期間は決まっているが、生産する順は任意

プログラム1

GLPKで動くプログラムです。
data;
より後の変更で多くの条件に対応できるようになっています。

manufacuring21.mod
# version 0.1
set WorkingDay; # 工場稼働日付を読み込む
set ProductName; # 製品名を読み込む
param manHourByProduct{ProductName}; # 製品を作るために必要な工数
set ManufacturablePeriod  dimen 2; # 生産可能日を読み込む
param manHourByDay{WorkingDay} default 100; # 1日の標準的な工数上限

var assign{ManufacturablePeriod} binary; # 生産日を割り当てるための変数
# 各製品の生産は1日だけ
s.t. product1{p in ProductName}: sum{(d,p) in ManufacturablePeriod}assign[d,p]==1;
# 1日の総工数を超えない
s.t. limitWorkerHour{d in WorkingDay}: sum{(d,p) in ManufacturablePeriod}assign[d,p]*manHourByProduct[p]<=manHourByDay[d];

solve;

# 結果を出力
for{d in WorkingDay}{
    printf "%s   総工数 %10.2f\n", d, sum{(d,p) in ManufacturablePeriod}assign[d,p]*manHourByProduct[p];
    printf "  製品名 工数\n";
    printf{(d,p) in ManufacturablePeriod: assign[d,p]==1} "  %s   %d\n", p, manHourByProduct[p];
}

data;
# 工場稼働日付
set WorkingDay := 1 2 4;
# 製品名と製造に必要な工数
param : ProductName : manHourByProduct:=
"製品1"  100
"製品2"   50
"製品3"   20
"製品4"   80
"製品5"   40
;
# 製品名と生産可能日
set ManufacturablePeriod :=
(*, "製品1") 1 2  # 製品1を1 2 日に限定
(*, "製品2") 2 4
(*, "製品3") 1 2
(*, "製品4") 2 4
(*, "製品5") 1 2 4
;
# 1日の工数上限を特別に変えたい場合
param manHourByDay[2] := 110;
end;

MathProgはかなり癖のある言語ですが、慣れると結構便利なのです。
簡単に補足しておきます。
data;
より上がプログラムの領域で、下がデータを書く領域です。それぞれ書き方が違いますので注意してください。
デバッグをするには、
display ManufacturablePeriod;
のようなdispaly文を追加することで、中身を表示することができます。
(ただし、varで宣言している変数はsolveの後でないと表示されません)

ManufacturablePeriodには製品ごとの生産が可能な日付が読み込まれています。

var assign{ManufacturablePeriod} binary;
バイナリ(0か1)が入る変数を宣言しています。生産を割り当てる場合は1,割り当てない時は0になります。
{}の中は添え字の集合を示しますが、ManufacturablePeriodが二次元ですので、C言語などの二次元配列変数に相当します。C言語などでは3日で製品が5つありますので、要素数は3x5=15個になりますが、ここではManufacturablePeriodで宣言した11個しかありません。添え字(4, "製品1")の要素は存在しません。

s.t. product1{p in ProductName}: sum{(d,p) in ManufacturablePeriod}assign[d,p]==1;
制約条件(s.t.)です。二重のループに相当します。{p in ProductName}が外側のループで、pに"製品1","製品2"...と順に値がpに入ります。
{(d,p) in ManufacturablePeriod}が内側のループで、ここのpは外側のp値で拘束されます。
p=="製品1"の時には、ManufacturablePeriodには(1,"製品1"),(2,"製品1")しかありませんので、内側のループはd=1と2だけ繰り返されassign[d,p]の合計が計算されます。assign[1,"製品1"]+assign[2,"製品1"]の合計が1になるようにassignに値が設定されます。
p=="製品2"....も同様です。

printf{(d,p) in ManufacturablePeriod: assign[d,p]==1}....
{}内の条件が満たされた時だけに印刷されます。: assign[d,p]==1はこの条件が満たされた時、すすなわちassignの要素が1(生産を割り当てる)時だけ印刷されます。

上のプログラムに条件を追加することで、複数の日に渡って生産したり、ロット分割など簡単(?)に機能を追加できますので、徐々に公開していきたいと思います。

質問、要望などがありましたらコメントしてください。

あまり良い例とは言えないように思いますが、以前作った関連したプログラムをリンクしておきます。
かなりのことができるはずですが、だいぶ前に作ったもので、思い出せない部分もあります。今後整理して公開していきたいと思います。

GLPKプログラムA
複数のロットに分割して、生産日を分けることができる。

GLPKプログラムB
結構複雑なことができることをだけは感じられると思います。(汗)

1
1
11

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
1