はじめに
表計算ソフトのソルバーを使った方法です。
やっていることは「生産現場で役に立つかもしれない最適化手法2」とほぼ同じですが、小さなロットに分割し複数の日に生産を分けられるようにしています。
最適化の条件
- 製品ごとに必要な工数が与えられており、日ごとの最大総工数以内になるように生産計画を立てたい。
- 各製品の生産はロットに分割できる
- 製品の製造できる期間は決まっているが、生産する順は任意
ソルバーで
LibreOffice Calcのソルバーでやってみました。
EXCELでも同じようにできるはずです。
まず文字や数値を次のように書き込みます。
セルG2に下図の式を入れ、下方向にG6までコピーします。ロット数の合計を計算しています。
セルD7に下図の式を入れ、右方向にF7までコピーします。工数の合計を計算しています。
G9に生産できない日に相当するセル(工数が0になる)の合計の計算式を入れます。
「変更させるセル」で変数領域のセルを指定します。ロット数が入りますので、
制約条件で、その領域のセルが整数でかつ0以上になるように制約します。
制約条件3行目はロット数の合計が一致するように
制約条件4行目で各日の工数の上限を超えないように制限しています。
どうも「ターゲットセル」を指定しないと動かないみたいなので、苦し紛れに入れています。
かなり遅くなっています。
できればG9は制約条件として=0にすればすっきにするのですが。
「ターゲットセル」を指定する必要がなければ、そのようにしてください。
これで実行すれば「変更させるセル」に数値(ロット数)が入ります。
GLPKプログラム
GLPKで動くプログラムも載せていきます。
同じことができます。
# version 0.2
set WorkingDay; # 工場稼働日付を読み込む
set ProductName; # 製品名を読み込む
param manHourPerLot{ProductName}; # 1ロットの工数
param nLotByProduct{ProductName}; # 製品ごとのロット数
set ManufacturablePeriod dimen 2; # 生産可能日を読み込む
param manHourByDay{WorkingDay} default 100; # 1日の標準的な工数上限
var assignLot{ManufacturablePeriod} integer, >=0 ; # 生産可能な日のロット数
# 各製品のロット数の合計を合わす
s.t. product1{p in ProductName}: sum{(d,p) in ManufacturablePeriod}assignLot[d,p]==nLotByProduct[p];
# 1日の総工数を超えない
s.t. limitWorkerHour{d in WorkingDay}: sum{(d,p) in ManufacturablePeriod}assignLot[d,p]*manHourPerLot[p]<=manHourByDay[d];
solve;
# 結果を出力
for{d in WorkingDay}{
printf "%s 総工数 %10.2f\n", d, sum{(d,p) in ManufacturablePeriod}assignLot[d,p]*manHourPerLot[p];
printf " 製品名 ロット数*ロット当たりの工数\n";
printf{(d,p) in ManufacturablePeriod: assignLot[d,p]>0} " %s %d * %d\n", p, assignLot[d,p], manHourPerLot[p];
}
data;
# 工場稼働日付
set WorkingDay := 1 2 4;
# 製品名と1ロットの工数
param : ProductName : manHourPerLot:=
"製品1" 25
"製品2" 25
"製品3" 20
"製品4" 40
"製品5" 20
;
# 製品ごとのロット数
param : nLotByProduct:=
"製品1" 4
"製品2" 2
"製品3" 1
"製品4" 2
"製品5" 2
;
# 製品名と生産可能日
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;