🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

1回答

2523閲覧

pulpを用いたシフト作成

K.takita

総合スコア14

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2019/12/22 14:33

前提・実現したいこと

python初心者です。
pulpを用いてシフトの作成を行いたいと思っています。
アルバイトが3人、日にちが1日、シフトパターンが4つ、時刻が1時間ごとに設けられており、それぞれ必要人数は2人です。アルバイトの希望出勤日がわかっており、0の場合は休み、1の場合は出勤可能を表しています(今回はすべて1ですが)。
この問題の目的は希望出勤日数(希望出勤日の総和)と実際に勤務した日の総和を最小にすることです。
制約として、以下の3つを考えています。
・実際に働いた日数が希望出勤日数を超えない制約
・各時刻の勤務人数が必要人数以上である制約
・入ることのできるシフトは1つだけである制約
これらのことを踏まえ作成してみたプログラムは以下の通りです。

python

1import numpy as np 2import pandas as pd 3from pulp import LpBinary, LpMinimize, LpProblem, LpVariable, lpSum, value 4 5#アルバイトの集合 6n_member = 3 7member = pd.Series(f"member{m+1}" for m in range(n_member)) 8 9#日にちの集合 10n_day = 1 11day = pd.Series(f"day{d+1}" for d in range(n_day)) 12 13#時刻の集合 14n_time = 9 15time = pd.Series(f"{t+12}時" for t in range(n_time)) 16 17#シフト番号 18n_shiftnumber = 4 19shiftnumber = pd.Series(f"shift{sn+1}" for sn in range(n_shiftnumber)) 20 21#シフトの一覧 22shift = pd.DataFrame([ 23 [1, 1, 1, 1, 1, 1, 0, 0, 0], 24 [0, 0, 0, 0, 1, 1, 1, 1, 1], 25 [1, 1, 1, 0, 0, 1, 1, 1, 1], 26 [1, 1, 1, 1, 0, 0, 1, 1, 1], 27 ], 28 index=shiftnumber, 29 columns=time 30 ) 31 32#必要人数 33need = pd.DataFrame([[2, 2, 2, 2, 2, 2, 2, 2, 2]], index=day, columns=time) 34 35#希望出勤日 36kibou = pd.DataFrame([[1], 37 [1], 38 [1]], index=member, columns=day) 39 40 41#モデルの作成 42prob = LpProblem(sense=LpMinimize) 43 44#変数 45x = [[[LpVariable(f"x{m}{d}{t}", cat=LpBinary) for t in shift] 46 for d in day] for m in member] 47 48 49#目的関数 50for m in range(member.size): 51 #希望出勤日数 52 ks = kibou.iloc[m].sum() 53 #実際に働いた日数 54 actual = lpSum(x[m][d] for d in range(day.size)) 55 #希望出勤日数と実際に働いた日数の差 56 prob += ks - actual 57 #(制約1)実際に働いた日数が希望出勤日数を超えない制約 58 prob += actual <= ks 59 60 61#制約条件 62#(制約2)必要最低人数を満たす制約 63for d in range(need.index.size): 64 for t in range(need.index.size): 65 prob += lpSum(x[m][d][t] for m in range(member.size)) >= need.iloc[d, t] 66 67#(制約4)各メンバーが1日に取りうるシフトは必ず1つ 68for m in range(member.size): 69 for d in range(day.size): 70 prob +=lpSum(x[m][d][t] for t in range(shift.index.size)) == 1 71 72prob.solve() 73 74 75scheduling = pd.DataFrame(index=member, columns=day) 76for m in range(member.size): 77 for d in range(day.size): 78 for i,j in enumerate(shift.index): 79 if value(x[m][d][i]): 80 scheduling.iloc[m, d] = j 81scheduling.to_csv("scheduling.csv") 82

私の欲しい答えとしては、shift1, shift2, shift4の3つがそれぞれのメンバーに振り分けられるようにしたいのですが、全員がshift1という結果になってしまいます。

つたない説明で申し訳ありません。どうすればよいかご教授のほどよろしくお願いします。

これに加えて、今後目的関数に、もし出勤可能な人数が必要人数を下回る場合に、ダミーのアルバイトを不足しているシフトにいれ、そのダミーの総和を加えるということもやりたいと思っています。よろしければこちらも答えていただけると幸いです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2019/12/23 13:55

やりたいことではなく、なぜ機能していないのかをきちんと考えてください 次に何をしたいとかより、今実現すべき機能が動いているように見えることを目指してください 動いているように見えたら、本当に正しく動いているのかあら探ししながら確認してください 完璧に動いていることが確認できたら、同じ機能を実現するために、実装をどう工夫した方がより良くなるのかを考えてください これ以上工夫できなくなったら、次に何をしたいかようやく考えることができます 今回はまず変数、制約、目標が正確に設定されているか調べてください print(prob)で見ることができます 日付の加算おかしくないですか? 制約の数おかしくないですか? これらはただのデバッグ/テストです。1人で出来るはずなので、ちゃんとやりましょう。
guest

回答1

0

コメントに返事ももらえずに長期間放置されてたので内容忘れました。

質問者がこの質問を放置したまま、ちょっと設定の違う新しい質問をするなどという暴挙に出ていた(目的関数の部分が相変わらず間違ってる)ので、こちらに当時書いていたコードだけ貼っときます。
何を修正したかも、これで合ってるのかも、もう忘れましたが、参考にでもなれば幸いです。

python

1import numpy as np 2import pandas as pd 3from pulp import LpBinary, LpMinimize, LpProblem, LpVariable, lpSum, value 4 5#アルバイトの集合 6n_member = 3 7member = pd.Series(f"member{m+1}" for m in range(n_member)) 8 9#日にちの集合 10n_day = 1 11day = pd.Series(f"day{d+1}" for d in range(n_day)) 12 13#時刻の集合 14n_time = 9 15time = pd.Series(f"{t+12}時" for t in range(n_time)) 16 17#シフト番号 18n_shiftnumber = 5 19shiftnumber = pd.Series(f"shift{sn+1}" for sn in range(n_shiftnumber)) 20 21#シフトの一覧 22shift = [ 23 [0, 0, 0, 0, 0, 0, 0, 0, 0], 24 [1, 1, 1, 1, 1, 1, 0, 0, 0], 25 [0, 0, 0, 0, 1, 1, 1, 1, 1], 26 [1, 1, 1, 0, 0, 1, 1, 1, 1], 27 [1, 1, 1, 1, 0, 0, 1, 1, 1], 28 ] 29dfshift = pd.DataFrame(shift, 30 index=shiftnumber, 31 columns=time 32 ) 33shiftdaycount = [int(sum(x) > 0) for x in shift] 34 35#必要人数 36need = pd.DataFrame([[2, 2, 2, 2, 2, 2, 2, 2, 2]], index=day, columns=time) 37 38#希望出勤日 39kibou = pd.DataFrame([[1], 40 [1], 41 [1]], index=member, columns=day) 42 43 44#モデルの作成 45prob = LpProblem(sense=LpMinimize) 46 47#変数 48shiftmds = LpVariable.dicts('shiftmds', ([m for m in range(member.size)], [d for d in range(day.size)], [s for s in range(n_shiftnumber)]), 0, 1, 'Integer') 49 50#目的関数 51prob += kibou.values.sum() - lpSum(shiftdaycount[s] * shiftmds[m][d][s] for s in range(n_shiftnumber) for d in range(day.size) for m in range(member.size)) 52 53for m in range(member.size): 54 #希望出勤日数 55 ks = kibou.iloc[m].sum() 56 #実際に働いた日数 57 actual = lpSum(shiftdaycount[s] * shiftmds[m][d][s] for s in range(n_shiftnumber) for d in range(day.size)) 58 #(制約1)実際に働いた日数が希望出勤日数を超えない制約 59 prob += actual <= ks 60 61 62#制約条件 63#(制約2)必要最低人数を満たす制約 64for d in range(day.size): 65 for t in range(time.size): 66 prob += lpSum(shift[s][t] * shiftmds[m][d][s] for s in range(n_shiftnumber) for m in range(member.size)) >= need.iloc[d, t] 67 68#(制約4)各メンバーが1日に取りうるシフトは必ず1つ 69for m in range(member.size): 70 for d in range(day.size): 71 prob += lpSum(shiftmds[m][d][s] for s in range(n_shiftnumber)) == 1 72 73prob.solve() 74 75 76scheduling = pd.DataFrame(index=member, columns=day) 77for m in range(member.size): 78 for d in range(day.size): 79 for s in range(n_shiftnumber): 80 if shiftmds[m][d][s].value() == 1: 81 scheduling.iloc[m, d] = shiftnumber[s] 82 83scheduling.to_csv("scheduling.csv")

※内容覚えてないし、コメントも解決済みも返事も全て不要です。マイナス評価にでもしといてください。

投稿2020/01/09 14:20

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問