2024年2月24日土曜日

同じペアが夜勤入りが一ヶ月で三回も一緒になる場合がある

<原因考察>

 通常、そんな事は起こりえないと思いますが、確かに解を見ると、そのような例が見受けられます。

そこで、制約を見ると、次のように階の被りがないように制約されています。


その結果、例えば、2階のスタッフのペアは、3階か4階のスタッフしかありえない訳です。
そうすると、3階か4階の夜勤可能なスタッフは、8人とかに限定されてしまいます。夜勤を6回やるとすると、確率的にありえない話ではなくなる、ということが分かりました。

<そもそも3回夜勤一緒になると何が問題なのか?>

という疑問が沸いてきます。夜勤経験のある方なら分かりますが、気の合うペアならば、何も問題ないです。しかし、気の合わないスタッフ、組みたくない相手である場合、3回の一緒、実質6日間も夜勤ペアならば、最悪の憂鬱となります。場合によっては、仕事を辞めたくなる要因になりえます。

従い、この問題は無視することが出来ない切実な問題、と考えられます。一方、一律で、1回に限定するとした場合、解空間を狭める、事が考えられます。この制約を入れたことによって、他の制約の成否に影響を与えたくありません。そこで、スタッフ毎に一緒になる回数を設定することにします。

一方「私は、誰とでもOKよ」というスタッフもいるでしょうし、上司からみて、皆から組むのを嫌がられているスタッフもいるでしょう。そこで、組むスタッフの一緒回数の最大をスタッフプロパティで設定するようにします。

選択は、1または2です。「私は、誰とでもOKよ」の人は、ブランクにします。どちらもブランクであれば、制約されません。どちらかが、ブランクでない場合は、ブランクでないスタッフの一緒回数で制約されます。どちらもブランクでないならば、小さいほうの数に制約されます。

実装は、次のPythonソースです。

def 夜勤一緒回数制約sub(p1,p2):
    min_num=最大夜勤一緒回数属性[p1]#p1のDICは、存在する p2のDICは、存在しないかもしれない
    for person in 最大夜勤一緒回数属性:
        if min_num> 最大夜勤一緒回数属性[p2]:
            min_num=最大夜勤一緒回数属性[p2]
    list=[]
    for day in 今月:
        v1=sc3.GetShiftVar(p1,day,'入り')
        v2=sc3.GetShiftVar(p2,day,'入り')
        list.append(v1&v2)#両方入りに入る
    s='夜勤一緒回数 '+staffdef[p1]+staffdef[p2]
    sc3.AddSoft(sc3.SeqError(0,min_num,2,list),s,5)#今月最大一緒回数
    
def 夜勤一緒回数制約():
    for person in 最大夜勤一緒回数属性:

        if person in a_2F:#2階は、3階と4階を見る
            if person not in 入り:
                continue
            for ペア3F in b_3F:
                if ペア3F not in 入り:
                    continue
                夜勤一緒回数制約sub(person,ペア3F)
            for ペア4F in c_4F:
                if ペア4F not in 入り:
                    continue
                夜勤一緒回数制約sub(person,ペア4F)

        if person in b_3F:#3階は、4階だけ見れば十分
            if person not in 入り:
                continue
            for ペア4F in c_4F:
                if ペア4F not in 入り:
                    continue
                夜勤一緒回数制約sub(person,ペア4F)

        #print(staffdef[person])
夜勤ペアのうち、小さいほうの数を、一緒回数の最大回数として設定しています。こうすれば、どちらかが、1回であれば、一緒回数は最大1回に制約されます。

<検証>
上のプログラムがきちんと動くかどうかを検証します。検証は、post_mainで記述します。

def check_夜勤一緒回数():
    for p1 in 最大夜勤一緒回数属性:
        for p2 in 最大夜勤一緒回数属性:
            if p1==p2:
                continue
            count=0
            max=最大夜勤一緒回数属性[p1]
            if max> 最大夜勤一緒回数属性[p2]:
                max=最大夜勤一緒回数属性[p2]
            for day in 今月:
                v1=shift_solution[p1][day]
                v2=shift_solution[p2][day]
                if v1=='入り' and v2=='入り':
                    count+=1
            if count>max:
                print("************Error",staffdef[p1],"と",staffdef[p2],"で夜勤一緒回数が、",count,"になっています。")
            else:
                print(staffdef[p1],"と",staffdef[p2],"で夜勤一緒回数が、",count,"になっています。")

def post_main():#求解後、呼び出しルーチン 
    if ダイナミック割り当てフロア長会議制約をEnable:
        check_フロア長会議(2)
    if 夜勤一緒回数制約をEnable:
        check_夜勤一緒回数()
求解後、次のように表示されます。
 _____________________________________
|           |           |             |
|   Weight  |   Errors  |    Cost     |
|___________|___________|_____________|
|           |           |             |
|         7 |         0 |           0 |
|         6 |         0 |           0 |
|         5 |         0 |           0 |
|         3 |         2 |           6 |
|___________|___________|_____________|
|                       |             |
|         Total         |           6 |
|_______________________|_____________|
	*********UB=6(0)  4.437(cpu sec)
o 6(0)
解探索が終了しました。 5 (秒)
解が得られました。
ポスト処理を実行します。ソルバを呼び出し中です。
 と  で夜勤一緒回数が、 0 になっています。
 と  で夜勤一緒回数が、 0 になっています。
 と  で夜勤一緒回数が、 0 になっています。
 と  で夜勤一緒回数が、 0 になっています。
 と  で夜勤一緒回数が、 0 になっています。
 と  で夜勤一緒回数が、 0 になっています。
 と  で夜勤一緒回数が、 0 になっています。
 と  で夜勤一緒回数が、 1 になっています。
 と  で夜勤一緒回数が、 0 になっています。
 と  で夜勤一緒回数が、 1 になっています。
 と  で夜勤一緒回数が、 1 になっています。
 と  で夜勤一緒回数が、 2 になっています。
 と  で夜勤一緒回数が、 0 になっています。
 と  で夜勤一緒回数が、 0 になっています。
 と  で夜勤一緒回数が、 0 になっています。
 と  で夜勤一緒回数が、 0 になっています。
 と  で夜勤一緒回数が、 0 になっています。
 と  で夜勤一緒回数が、 0 になっています。


https://www.nurse-scheduling-software.com/japanese/constraints_faqs/chapter34/

0 件のコメント:

コメントを投稿