OptaPlanner examples その4

Dinner party


パーティの席決めです。様々な条件を考慮して各テーブルに参加者を最適な組み合わせで配置する。。。そう、結婚式で一度だけ経験しましたよ。
このサンプルでは、男女が交互に並ぶ、隣同士が共通の趣味を持つ、1テーブルに職業を適正なバランスで配置する、という制約が与えられています。

  • DinnerParty : @PlanningSolution
    • List getSeatList() : @ValueRangeProvider(id = "seatRange")
    • List getSeatDesignationList() : @PlanningEntityCollectionProperty
  • SeatDesignation : @PlanningEntity
    • Seat getSeat() : @PlanningVariable(valueRangeProviderRefs = {"seatRange"})
    • Guest guest

SeatDesignation は Guest と Seat の組み合わせを保持し、「誰がどの席に座るか」を示すエンティティ。比較的単純ですね。SeatDesignation は 1-1 で固定の Guest を保持する(variable ではない)ため、Guest を PlanningEntity にするようなモデリングもアリなはず。Gender、Job、JobType、Hobby、HobbyPractician は、Guest に関連するクラス。モデリングの際に、比較的細かい粒度でクラス化されている印象。おそらくクラスにした方がルールが書きやすいからだろう。

ルールの1例。

rule "twoSameJobTypePerTable"
    when
        $jobType : JobType()
        $table : Table()
        not (
            SeatDesignation(guestJobType == $jobType, seatTable == $table, $leftId : id, $firstJob : guestJob)
            and SeatDesignation(guestJobType == $jobType, seatTable == $table, id > $leftId,
                    differentKindIfNeeded($firstJob))
        )
    then
        scoreHolder.addConstraintMatch(kcontext, -100);
end

あれ?そういえばなんで JobType みたいなファクトが insert されるのだろう。どれがルールで使用するファクトだって明示してたっけ?

http://docs.jboss.org/optaplanner/release/latest/optaplanner-docs/html_single/index.html#planningProblemAndPlanningSolution

getProblemFacts() でした。ルール以外のスコアリング方法の時は必要無いそうです。

この example は DRL だけじゃなく、XSL(dinnerPartyExtraScoreRules.xls)にも追加の細かい席決めルールが記述されています。「DemocratとRepublicanは隣にしてはダメ」とか。dinnerPartyExtraScoreRules.xls の読み込み指定は dinnerPartySolverConfig.xml に一行追加するだけ。

  <scoreDirectorFactory>
    <scoreDefinitionType>SIMPLE</scoreDefinitionType>
    <scoreDrl>org/optaplanner/examples/dinnerparty/solver/dinnerPartyScoreRules.drl</scoreDrl>
    <scoreDrl>org/optaplanner/examples/dinnerparty/solver/dinnerPartyExtraScoreRules.xls</scoreDrl>
  </scoreDirectorFactory>