第17章 計画パターン
17.1. 計画パターンの導入
ここで挙げる計画パターンは、計画時に発生する一般的な課題をリストアップして解決します。
17.2. 計画エンティティへの時間の割り当て
時間と日程は、ユースケースのニーズによって異なるため、計画問題で時間と日数を取り組むのは簡単ではありません。
Java には、タイムスタンプ、日数、期間を表現する方法はいくつかあります。ユースケースに適した表現を選択します。
-
java.util.Date
(deprecated): タイムスタンプの表示方法。時間がかかり、間違いが起こりやすいため、使用しないでください。 javax.time.LocalDateTime
、LocalDate
、DayOfWeek
、Duration
、Period
など: タイムスタンプや日数などを正確に表示して計算する方法。- タイムゾーンと夏時間 (DST) に対応します。
Java 8 以降が必要です。
- Java 7 の場合は、ThreeTen Backport という名前のバックポートを使用します。
- Java 6 以前の場合は、代わりに前身となる Joda-Time を使用します。
int
またはlong
: 粒度の粗い時間単位 (分など) の簡易数値として、グローバル計画の時間枠またはエポックの開始時からのタイムスタンプをキャッシュに格納します。-
たとえば、
LocalDateTime
の1-JAN 08:00:00
と1-JAN 09:00:00
は、int
ではそれぞれ400
分と460
分になります。 -
これは、しばしばクラスの別フィールドと、それから計算した
LocalDateTime
フィールドを表示します。ユーザーへの可視化にはLocalDateTime
が使用されますが、スコア制約にはint
が使用されます。 - 計算が速く、特に TimeGrain pattern で便利です。
- タイムゾーンまたは DST がスコア制約に影響する場合は使用しないでください。
-
たとえば、
また、計画エンティティを開始時間 (または開始日) に割り当てる計画がいくつかあります。
開始時間が前もって決められています。(そのような Solverでは) これはプランニング変数ではありません。
- たとえば、病床計画 例では、患者が入院する日は前もって決まっています。
- これは、多段階計画ではよくあることで、計画の前段階ですでに開始時間が決まっています。
開始時間が固定されていない場合は、プランニング変数です (正規またはシャドウ)。
すべての計画エンティティの期間が同じ場合は、Timeslot パターン を使用します。
- たとえば、コースの時間割では、講義の長さはすべて 1 時間なので、時間割の長さは 1 時間になります。
長さが異なり、特定の間隔 (たとえば 5 分間隔) で区切る場合は、TimeGrain パターンを使用します。
- たとえば、会議のスケジューリングでは、すべての会議が 15 分間隔で開始し、長さは 15 分、30 分、45 分、60 分、90 分、または 120 分になります。
長さが異なり、(同じ実行者に割り当てられた) 前のタスクが終了したらすぐに別のタスクが開始する場合は、Chained Through Time パターンを使用します。
- たとえば、時間枠での車両の決定では、前の顧客への配送が終了したらすぐに次の顧客に向かいます。
このユースケースに適切なパターンを選択します。
17.2.1. Timeslot パターン: 固定長の時間枠に割り当て
プランニングエンティティーの期間がすべて同じ (または、同じ期間に拡張できる) 場合は、時間枠パターンが便利です。プランニングエンティティーを、時間ではなく時間枠に割り当てます。たとえば、コースの時間割では、すべての授業が 1 時間になります。
時間枠はいつでも開始できます。たとえば、時間枠を 8:00、9:00、(15 分の休憩)、10:15、11:15 に開始します。重ねることはできますが、通常は重ねません。
また、すべてのプランニングエンティティーを同じ期間に拡張することができます。たとえば 試験の時間割では、90 分のものもあれば、120 分のものもありますが、時間枠はすべて 120 分です。90 分の試験を時間枠に割り当てる場合、残りの 30 分は席が予約されたままになり、別の試験で使用することはできません。
通常、2 つ目のプランニング変数 (たとえば部屋) があります。コースの時間割では、2 つの授業が同じ部屋を同じ時間枠で共有すると競合しますが、試験の時間割では、その部屋に席が余っていれば許可されます (ただし、同じ部屋で複数の試験を行う期間では、ソフトスコアペナルティーが課せられます)。
17.2.2. TimeGrain パターン: 開始 TimeGrain に割り当て
9 時 4 秒に会議を開始するように人を割り当てるのは役に立ちません。なぜなら、人の活動における時間粒度は 5 分または 15 分だからです。したがって、プランニングエンティティーを、1 秒未満、1 秒、または 1 分の精度で割り当てる必要はありません。5 分または 15 分の精度で十分です。TimeGrain パターンは、時間を時間粒として分割することでこのような時間精度を作ります。たとえば、会議のスケジュール では、すべての会議は 1 時間、30 分、または各時 15 分前後の間隔で始まるため、時間粒度の最適な設定は 15 分になります。
各プランニングエンティティーは、開始時間粒に割り当てられます。終了時間粒は、開始時間粒に追加して計算します。2 つのエンティティーが重複するかどうかは、開始時間粒と終了時間粒を比較することで決まります。
このパターンは、時間粒度が粗くても (1 日、半日、数時間など) 機能します。時間粒度が細かく (秒、ミリ秒など)、時間枠が長い場合、値の範囲 (および 探索空間) は高くなりすぎて、効率とスケーラビリティーが低くなります。ただし、コストを抑えるスケジュール で示すように、このような解は不可能ではありません。
17.2.3. Chained Through Time パターン: 開始時間を決める連鎖に割り当て
人間またはマシンが、順番に、一度に 1 つのタスクを継続して取り組む場合 (以前のタスクが終了したらタスクが開始したり、決定論的遅延がある場合)、Chained Through Time パターンは役に立ちます。たとえば、時間枠がある配車例では、車が顧客から顧客に移動します (したがって一度に 1 人の顧客を処理します)。
このパターンでは、プランニングエンティティーは 連鎖 となります。アンカーは、最初のプランニングエンティティーの開始時を決めます。2 番目のエンティティーの開始時間は、最初のエンティティーの開始時間と期間に基づいて計算されます。たとえば、タスク割り当てで、Beth (アンカー) は 8:00 に働き始め、1 つ目の仕事を 8:00 に開始します。これは 52 分続くため、2 番目の仕事は 8:52 に開始します。エンティティーの開始時間は通常 シャドウ変数 です。
アンカーには連鎖が 1 つだけあります。アンカーは 2 つに分割することができます (たとえば、Beth は同時に 2 つのタスクを行うことができるため、Beth を Beth の左手と、Beth の右手に分けます) が、このモデルでは予備のリソースを持つことが難しくなります。したがって、試験の時間割例でこのモデルを使用して、同じ時間に同じ部屋を使うように 2 つ以上の試験を割り当てることは問題となります。
プランニングエンティティーでは、ギャップを作成する方法は 3 つあります。
- ギャップなし: これは、アンカーがマシンの場合に一般的です。たとえば、ビルドサーバーは、1 つのジョブを終了したら、常に中断せずに次のジョブを開始します。
決定論的ギャップ: これは、人間の場合に一般的です。たとえば、10:00 の境界を超えるすべてのタスクでは、15 分間余分に割り当てることで、休憩が取れます。
- 決定論的ギャップは、複雑なビジネスロジックに従う場合があります。たとえば、配車例では、大陸を横断するトラック運転手は、運転を 2 時間続けたら 15 分の休憩が必要になります (これは、顧客の場所で荷物の積み下ろしている間も適用されます)。また、14 時間働いたら 10 時間の休むことが必要になります。
- プランニング変数のギャップ: これは一般的ではありません。(探索空間 に影響する) プランニング変数が過剰に追加されることで、効率性とスケーラビリティーが減るためです。
17.3. 多段階計画
(コンウェイの法則 などの) 実用的または組織的な理由により、複合的な計画問題を複数の段階に分類する場合がしばしばあります。典型的な例として、電車のスケジューリングが挙げられます。ある部署が、電車の発着場所と時間を決め、別の部署が、列車の車両や機関車にどの運転手を割り当てるかを決定します。
各段階に特有の Solver 設定 (つまり、特有の SolverFactory
) があります。Solver 設定を 1 つだけ使用する 多相解決 とは混合しないでください。
分割検索 と同様、多段階計画は準最適な結果となります。それでもなお、メンテナンスや所有者を簡略化し、プロジェクトの立ち上げに貢献するのに役に立つ場合があります。