組合せ最適化 - 典型問題 - 勤務スケジューリング問題

[典型問題と実行方法](0f6c1a4415d196e64314)

##勤務スケジューリング問題

スタッフの人数、スケジュール日数、シフトの種類数、避けるべきシフトのパターン、日ごとのシフトごとの必要数が与えられたときに、これらを満たすスケジュールを求めよ。
目的関数や制約には、様々なバリエーションがある。

##実行方法

```text:usage
Signature: shift_scheduling(ndy, nst, shift, proh, need)
Docstring:
勤務スケジューリング問題
入力
    ndy: 日数
    nst: スタッフ数
    shift: シフト(1文字)のリスト
    proh: 禁止パターン(シフトの文字列)のリスト
    need: シフトごとの必要人数リスト(日ごと)
出力
    日ごとスタッフごとのシフトの番号のテーブル
```

```python:python
from ortoolpy import shift_scheduling
ndy, nst = 8, 4
shift = '休日夜'
proh = ['夜夜', '夜日', '日日日']
need = {'日':[2] * 8, '夜':[1] * 8}
r = shift_scheduling(ndy, nst, shift, proh, need)
print(r)

import numpy as np, pandas as pd
a = pd.DataFrame(np.vectorize(lambda i: shift[i])(r),
    columns=[chr(65+i) for i in range(nst)],
    index=['%d日目'%i for i in range(1,ndy+1)])
for sft,lst in need.items():
    a['%s必要'%sft] = lst
    a['%s計画'%sft] = (a.iloc[:,:4]==sft).sum(1)
print(a)
```

```text:結果
[[0, 1, 2, 1],
 [1, 2, 0, 1],
 [1, 0, 1, 2],
 [2, 1, 1, 0],
 [0, 1, 2, 1],
 [1, 2, 0, 1],
 [1, 0, 1, 2],
 [2, 1, 1, 0]]

       A   B   C   D  日必要  日計画  夜必要  夜計画
1日目  休  日  夜  日      2      2      1      1
2日目  日  夜  休  日      2      2      1      1
3日目  日  休  日  夜      2      2      1      1
4日目  夜  日  日  休      2      2      1      1
5日目  休  日  夜  日      2      2      1      1
6日目  日  夜  休  日      2      2      1      1
7日目  日  休  日  夜      2      2      1      1
8日目  夜  日  日  休      2      2      1      1
```

```python:python
# pandas.DataFrame
from ortoolpy.optimization import ShiftScheduling
ShiftScheduling(8, 4, '休日夜', ['夜夜','夜日','日日日'], {'日':[2]*8, '夜':[1]*8})
```

<table>
  <thead>
    <tr>
      <th></th>
      <th>A</th>
      <th>B</th>
      <th>C</th>
      <th>D</th>
      <th>日必要</th>
      <th>日計画</th>
      <th>夜必要</th>
      <th>夜計画</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>1日目</th>
      <td>休</td>
      <td>日</td>
      <td>夜</td>
      <td>日</td>
      <td>2</td>
      <td>2</td>
      <td>1</td>
      <td>1</td>
    </tr>
    <tr>
      <th>2日目</th>
      <td>日</td>
      <td>夜</td>
      <td>休</td>
      <td>日</td>
      <td>2</td>
      <td>2</td>
      <td>1</td>
      <td>1</td>
    </tr>
    <tr>
      <th>3日目</th>
      <td>日</td>
      <td>休</td>
      <td>日</td>
      <td>夜</td>
      <td>2</td>
      <td>2</td>
      <td>1</td>
      <td>1</td>
    </tr>
    <tr>
      <th>4日目</th>
      <td>夜</td>
      <td>日</td>
      <td>日</td>
      <td>休</td>
      <td>2</td>
      <td>2</td>
      <td>1</td>
      <td>1</td>
    </tr>
    <tr>
      <th>5日目</th>
      <td>休</td>
      <td>日</td>
      <td>夜</td>
      <td>日</td>
      <td>2</td>
      <td>2</td>
      <td>1</td>
      <td>1</td>
    </tr>
    <tr>
      <th>6日目</th>
      <td>日</td>
      <td>夜</td>
      <td>休</td>
      <td>日</td>
      <td>2</td>
      <td>2</td>
      <td>1</td>
      <td>1</td>
    </tr>
    <tr>
      <th>7日目</th>
      <td>日</td>
      <td>休</td>
      <td>日</td>
      <td>夜</td>
      <td>2</td>
      <td>2</td>
      <td>1</td>
      <td>1</td>
    </tr>
    <tr>
      <th>8日目</th>
      <td>夜</td>
      <td>日</td>
      <td>日</td>
      <td>休</td>
      <td>2</td>
      <td>2</td>
      <td>1</td>
      <td>1</td>
    </tr>
  </tbody>
</table>