PlanningSolution类讲解

内容概要
通过这篇文章,我们来学习将ProblemFact、PlanningEntity连接在一起以及负责与OptaPlanner交互的Solution类。
ProblemFact实例
一个规划问题的数据集需要被包含在一个类中,供求解器求解。该解决方案类同时代表了规划问题和(求解结束后)解决方案。它被注解为@PlanningSolution注解。 例如,云资源优化的例子中,解决方案类是CloudBalance类,它包含一个computerList列表,一个processList列表。

@PlanningSolution @XStreamAlias("CloudBalance") public class CloudBalance extends AbstractPersistable { private List computerList; private List processList; ...... } 复制代码

一个规划问题实际上是一个未解决的规划解决方案,或者换个说法,一个未初始化的解决方案。
例如,在云资源优化例子中,那个CloudBalance类有@PlanningSolution注解,然而未解决的processList类中的每个CloudProcess都还没有被分配到一个计算机(他们的computer属性为空)。这不是一个可行的解决方案,它甚至不是一个可能的解决方案,这是一个未初始化的解决方案。
Solution类
一个Solution类持有所有的ProblemFact、PlanningEntity和一个Score。添加@PlanningSolution注解。例如,一个loudBalance实例持有所有计算机、进程的列表。
@PlanningSolution @XStreamAlias("CloudBalance") public class CloudBalance extends AbstractPersistable { private List computerList; private List processList; ...... } 复制代码

求解器配置需要声明规划解决方案类:
xsi:schemaLocation="https://www.optaplanner.org/xsd/solver https://www.optaplanner.org/xsd/solver/solver.xsd">

...
org.optaplanner.examples.nqueens.domain.NQueens
...

复制代码
Solution中的PlanningEntity
OptaPlanner需要从Solution实例中提取PlanningEntity实例。它通过调用每一个被@PlanningEntityCollectionProperty注释的getter(或字段)来获得这些集合。
@PlanningSolution public class NQueens { ...private List queenList; @PlanningEntityCollectionProperty public List getQueenList() { return queenList; }} 复制代码

可以有多个@PlanningEntityCollectionProperty注解的属性。那些甚至可以返回一个具有相同实体类类型的集合,也可以返回一个数组。
@PlanningEntityCollectionProperty注解需要在具有@PlanningSolution注解的类中的一个属性上。它在没有该注解的父类或子类中被忽略。
在极少数情况下,一个规划实体可能是一个属性:在其getter方法(或字段)上使用@PlanningEntityProperty来代替。如果启用,这两个注解也可以被自动发现。
Solution中的Score
一个@PlanningSolution类需要一个分数属性(或字段),需要添加@PlanningScore注解。如果分数还没有被计算,那么分数属性就是空的。分数属性的类型与你的用例的具体分数实现有关。例如,NQueens使用一个SimpleScore,CloudBalance使用的是HardSoftScore。
@PlanningSolution public class NQueens { ...private SimpleScore score; @PlanningScore public SimpleScore getScore() { return score; } public void setScore(SimpleScore score) { this.score = score; }} 复制代码

大多数用例都使用HardSoftScore:
@PlanningSolution public class CloudBalance { ...private HardSoftScore score; @PlanningScore public HardSoftScore getScore() { return score; }public void setScore(HardSoftScore score) { this.score = score; }} 复制代码

有些用例会使用其他分数类型。
Solution中的ProblemFact
对于约束流和Drools分数计算,OptaPlanner需要从Solution实例中提取ProblemFact实例。它通过调用每一个带有@ProblemFactCollectionProperty注解的方法(或字段)来获得这些集合。所有由这些方法返回的对象都可以被约束流或Drools规则所使用。例如,在CloudBalance中,所有的computerList是问题事实。
@PlanningSolution @XStreamAlias("CloudBalance") public class CloudBalance extends AbstractPersistable {private List computerList; private List processList; @XStreamConverter(HardSoftScoreXStreamConverter.class) private HardSoftScore score; public CloudBalance() { }public CloudBalance(long id, List computerList, List processList) { super(id); this.computerList = computerList; this.processList = processList; }@ValueRangeProvider(id = "computerRange") @ProblemFactCollectionProperty public List getComputerList() { return computerList; }public void setComputerList(List computerList) { this.computerList = computerList; }@PlanningEntityCollectionProperty public List getProcessList() { return processList; } } 复制代码

所有的ProblemFact都会自动插入到Drools工作存内存,请注意在它们的属性上添加注解。
而且可以有多个@ProblemFactCollectionProperty注解的成属性。这些属性甚至可以返回具有相同类别类型的集合,但它们不应该两次返回相同的实例,它也可以返回一个数组。
@ProblemFactCollectionProperty注解需要在一个有@PlanningSolution注解的类中的成员上。它在父类或没有该注解的子类上会被忽略。
在极少数情况下,ProblemFact可能是一个对象:在其方法(或字段)上使用@ProblemFactProperty代替。
扩展ProblemFact
有些ProblemFact一开始并未在业务模型中的体现,但是这些ProblemFact数据可以简化约束规则的编写,在提交求解之前,将这些数据计算出来作为一个ProblemFact放在Solution类内,可以是求解器更快、更简单的进行求解。
例如,在考试中,每两个至少共享一个学生的考试科目,就会创建一个的ProblemFact问题事实
TopicConflict。@ProblemFactCollectionProperty private List calculateTopicConflictList() { List topicConflictList = new ArrayList(); for (Topic leftTopic : topicList) { for (Topic rightTopic : topicList) { if (leftTopic.getId() < rightTopic.getId()) { int studentSize = 0; for (Student student : leftTopic.getStudentList()) { if (rightTopic.getStudentList().contains(student)) { studentSize++; } } if (studentSize > 0) { topicConflictList.add(new TopicConflict(leftTopic, rightTopic, studentSize)); } } } } return topicConflictList; } 复制代码

当分数约束需要检查是否有两个具有共同学生的题目的考试被安排在一起(取决于约束:在同一时间,在一排,或在同一天),TopicConflict实例可以被用作问题事实,而不是必须结合每两个学生实例,在求解过程中去计算,这样会大大降低OptaPlanner求解的效率。
自动扫描属性
与其明确配置每个属性(或字段)注解,有些也可以由OptaPlanner自动推导出来。例如,在CloudBalance的例子中:
@PlanningSolution(autoDiscoverMemberType = AutoDiscoverMemberType.FIELD)public class CloudBalance {// 自动发现为@ProblemFactCollectionProperty @ValueRangeProvider(id = "computerRange") // Not (yet) auto discovered private List computerList; // 自动发现为@PlanningEntityCollectionProperty private List processList; // 自动发现为 @PlanningScore private HardSoftScore score; ... } 复制代码

AutoDiscoverMemberType支持:
NONE:没有自动发现。
FIELD:自动发现@PlanningSolution类中的所有字段。
GETTER:自动发现@PlanningSolution类上的所有getter。
自动注解是基于字段类型(或getter返回类型):
@ProblemFactProperty:当它不是一个集合、一个数组、一个@PlanningEntity类或一个分数时。
@ProblemFactCollectionProperty:当它是一个不是@PlanningEntity类的集合(或数组)的类型时。
@PlanningEntityProperty:当它是一个配置的@PlanningEntity类或子类时。
@PlanningEntityCollectionProperty:当它是一个类型为配置的@PlanningEntity类或子类的集合(或数组)。
@PlanningScore:当它是一个分数或子类时。
最后
如果你觉得此文对你有一丁点帮助,点个赞。或者可以加入我的开发交流群:1025263163相互学习,我们会有专业的技术答疑解惑
【PlanningSolution类讲解】如果你觉得这篇文章对你有点用的话,麻烦请给我们的开源项目点点star: http://github.crmeb.net/u/defu 不胜感激 !

    推荐阅读