Tennis club scheduling
OptaPlanner training (http://d.hatena.ne.jp/tokobayashi/20141027) をやってないひとはここを読む前に、先に training をやりましょう!
ここまでが「Basic examples」です。今回は日時、人、リソースを要素としたプランニングの基礎パターンと言えそうです。
各チームに公平にテニスコートを使ってもらい、またできるだけ対戦相手が偏らないようにする、という制約です。「公平さ」をどうルールで表現するかの例題でもあります。
- TennisSolution : @PlanningSolution
- List
getTeamList() : @ValueRangeProvider(id = "teamRange") - List
getTeamAssignmentList() : @PlanningEntityCollectionProperty
- List
- TeamAssignment : @PlanningEntity
- Team getTeam() : @PlanningVariable(valueRangeProviderRefs = {"teamRange"})
- Day day
- int indexInDay
Team と Day が N-N の関係になるので、間に TeamAssignment を用意して @PlanningEntity にする、という基本パターン。一日に何チームがコートを使えるか、というのは indexInDay で表現している。複雑なケースだとここもクラス化されるはず。
「各チームが公平にテニスコートを使う」は「対戦相手が偏らないようにする」より重要な制約なので、ここでは HardMediumSoftScore という制約3段構えが来ました。Hard は絶対破ってはダメ。Medium は破ってもいいけど、Medium の点数は Soft の点数より絶対的に重視される。つまりいくら Soft が良くても、Medium の点数が下がれば採用されない。
「公平さ」を表現する「Fairness score」はここを読もう。 http://docs.jboss.org/optaplanner/release/latest/optaplanner-docs/html_single/index.html#fairnessScoreConstraints
まあ簡単に言えば、「自乗してマイナス」。
rule "fairAssignmentCountPerTeam" when $t : Team() $total : Number() from accumulate( $a : TeamAssignment(team == $t), count($a) ) then scoreHolder.addMediumConstraintMatch(kcontext, - ($total.intValue() * $total.intValue())); end