LoginSignup
0
0

More than 1 year has passed since last update.

勤務シフト表の作成 基礎(4)【GLPK】

Last updated at Posted at 2021-10-15

<--目次へ

2021.10.16 変更あり

勤務シフト表の作成 基礎(3)からの続きです。そちらも合わせてご覧ください。

先に書いた「shift11.mod」は業務の割り当て回数は設定通りにできますが、日程的に偏りがでる可能性がありますので、その対策です。

少し大きなデータ(下記)を用意しました。「勤務シフト表の作成 基礎(1)」のコメント欄あるデータを使用させていただきました。

(2021.10.16 変更)

業務の割り当てが分散するように

ある連続した期間に業務Aと業務Bを1回ずつ割り当てる制約を加えてみました。
連続した5日間(period1の値)に1回(maxFreqInPeriod1の値)を超えて割り当てないようにしています。「shift11.mod」の「solve;」の上に追加すると機能します。
業務ごとに別々に制約が適用されます。出勤希望が条件を満たさない(例えば5日間に2回の希望)があった場合でも希望を優先して割り当てられますが、自動的に割り当てる勤務については連続した5日間に1回以下を満たすようになっています。
period1やmaxFreqInPeriod1を変更すことで条件を変えることができます。
下の方に「最大連続勤務日数」の設定がありますが、必要があればそちらも変更してください。

period1
param period1 := 5;
param maxFreqInPeriod1 := 1;
s.t. constrainP1{s in Staff, d in firstDate..lastDate-(period1-1), b in Duty}:
    sum{d1 in d..d+(period1-1) : not (s,d1,b) in FixOnDuty}assignShiftSchedule[d1,s,b]+min(sum{d1 in d..d+(period1-1) : (s,d1,b) in FixOnDuty}1,maxFreqInPeriod1)<=maxFreqInPeriod1;

次が、業務を区別せずに連続した日に割り当てられない制約条件です。連続したした2日間(period2の値)に1回(maxFreqInPeriod2の値)以下になるように割り当てられます。業務を区別しないことを除いて上と同じです。

period2
param period2 := 2;
param maxFreqInPeriod2 := 1;
s.t. constrainP2{s in Staff, d in firstDate..lastDate-(period2-1)}:
    sum{d1 in d..d+(period2-1), b in Duty : not (s,d1, b) in FixOnDuty}assignShiftSchedule[d1,s,b]+min(sum{d1 in d..d+(period2-1), b in Duty : (s,d1, b) in FixOnDuty}1, maxFreqInPeriod2)<=maxFreqInPeriod2;

この2つを加えることで、両方の条件を満たすシフトが完成します。

条件を満たす組み合わせが見つからなかった場合には「LP HAS NO PRIMAL FEASIBLE SOLUTION」と出力されて止まってしまいます。period1などを小さくするなど条件を変えてみてください。

使用したデータ

「shift11.mod」のdata;より下を以下に書き換えてください。

data;
# 期間の最初の日付と最後の日付
param firstDate :=  1;
param lastDate := 29;

# 業務名
set Duty := "業務A" "業務B";

# スタッフ名
set Staff := "Aさん" "Bさん" "Cさん" "Dさん"
    "Eさん" "Fさん" "Gさん"
;

# 日付、業務別の必要出勤人数 defaultを1とし、それ以外の場合は後で指定
param necessary_nStaff default 1 :=
;

# スタッフ、業務別の最大割り当て勤務日数
param max_nWorkingDays default 4 :=
"Eさん"  "業務A" 5
"Cさん"  "業務B" 5
;
# スタッフ、業務別の最少割り当て勤務日数
param min_nWorkingDays default 4 :=
;

# 休日希望
set FixOffDuty :=
("Aさん", *)
("Bさん", *) 
("Cさん", *) 4 6 11 18 25
("Dさん", *) 5 12 15 1927
("Eさん", *) 1 6 20 25 29
("Fさん", *) 1 5 11 19 22 26
("Gさん", *) 4 8 13 22 27
;

# 出勤希望
set FixOnDuty :=
("Aさん", *, "業務A")  2 12 16 22
("Aさん", *, "業務B")  4 13 18 26
("Bさん", *, "業務A")  6 13 20 26 
("Bさん", *, "業務B")  2  9 21 25
;

# 最大連続勤務日数
param maxContinuousWorkDay default 5:=
;

# 期間前の連続勤務日数
param daysFromOffDutyBeforeFirstDay default 0:=
;
0
0
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
0
0