Java Optaplanner约束流
我正在与“更高级别”的约束流(bi、tri..)作斗争。Optaplanner用户指南中显示了最简单的案例,我理解它们。同样,通过这些例子,我能够对一个双CONSTRAINTSTREAM进行分组求和。 现在我遇到了一个棘手的问题。有人知道更好地理解ConstraintStreams的好信息源吗 我的案例:问题结构与运输问题非常相似。我有节点和边,想计算边的数量 我的主要问题是:Java Optaplanner约束流,java,optaplanner,Java,Optaplanner,我正在与“更高级别”的约束流(bi、tri..)作斗争。Optaplanner用户指南中显示了最简单的案例,我理解它们。同样,通过这些例子,我能够对一个双CONSTRAINTSTREAM进行分组求和。 现在我遇到了一个棘手的问题。有人知道更好地理解ConstraintStreams的好信息源吗 我的案例:问题结构与运输问题非常相似。我有节点和边,想计算边的数量 我的主要问题是: class Node{ UUID id; int itemsInInventory; ... }
class Node{
UUID id;
int itemsInInventory;
...
}
以及规划实体:
@PlanningEntity
class Edge{
Node from;
Node to;
@PlanningVariable
int itemsTransported;
getFromId(){return from.getId()}
getToId(){return to.getId()}
}
我想将边连接两次到节点中,然后groupBy它们确实有一个类似于(Node,Sum(to),Sum(from))的对象。
约束的当前代码为:
public Constraint minimizeShortage(ConstraintFactory constraintFactory) {
return constraintFactory.fromUnfiltered(Node.class)
.join(Edge.class,
equal(Node::getId, Edge::getFromId))
.join(Edge.class,
equal((node, edge) -> node.getId(), Edge::getToId))
groupBy(...)
}
对于BiConstraintStream(仅使用fromNode或toNode),groupBy将是
。groupBy((node,edge)->node,sum((node,edge)->edge.getItemsTransported())
,但我在第二次连接之后没有得到TriConstraintStream。我找到了解决问题的方法。解决方案是使用下面的语法,尤其是不要在一个groupBy中进行总结。我必须将求和分为两个GroupBy,并按以下顺序计算:Join1-groupBy1-join2-groupBy2
public Constraint minimizeShortage(ConstraintFactory constraintFactory) {
return constraintFactory.fromUnfiltered(Node.class)
.join(Edge.class,
equal(Node::getId, Edge::getFromId))
.groupBy((node, edgeFrom) -> node,
sum((node, tsaO) -> edgeFrom.transportedItems()))
.join(Edge.class,
equal((node, edge) -> node.getId(), Edge::getToId))
.groupBy((node, edgeFrom, edgeTo) -> node,
(node, edgeFrom, edgeTo) -> qtO,
sum((node, qtO, edgeTo) -> edgeTo.transportedItems()))
...
}
我找到了解决问题的办法。解决方案是使用下面的语法,尤其是不要在一个groupBy中进行总结。我必须将求和分为两个GroupBy,并按以下顺序计算:Join1-groupBy1-join2-groupBy2
public Constraint minimizeShortage(ConstraintFactory constraintFactory) {
return constraintFactory.fromUnfiltered(Node.class)
.join(Edge.class,
equal(Node::getId, Edge::getFromId))
.groupBy((node, edgeFrom) -> node,
sum((node, tsaO) -> edgeFrom.transportedItems()))
.join(Edge.class,
equal((node, edge) -> node.getId(), Edge::getToId))
.groupBy((node, edgeFrom, edgeTo) -> node,
(node, edgeFrom, edgeTo) -> qtO,
sum((node, qtO, edgeTo) -> edgeTo.transportedItems()))
...
}
我将以前面答案中的代码作为起点:
public Constraint minimizeShortage(ConstraintFactory constraintFactory) {
return constraintFactory.fromUnfiltered(Node.class)
.join(Edge.class,
equal(Node::getId, Edge::getFromId))
.groupBy((node, edgeFrom) -> node,
sum((node, tsaO) -> edgeFrom.transportedItems()))
.join(Edge.class,
equal((node, edge) -> node.getId(), Edge::getToId))
.groupBy((node, edgeFrom, edgeTo) -> node,
(node, edgeFrom, edgeTo) -> qtO,
sum((node, qtO, edgeTo) -> edgeTo.transportedItems()))
...
}
这段代码不太可能执行得很好,因为groupBy()
非常昂贵,将它们链接起来会使问题逐渐恶化。让我们看看是否可以只使用一个groupBy()
:
让我们再讨论一下。这个特殊的groupBy()
重载由两部分组成:键映射和收集器
键映射将三元组拆分为组-在这种情况下((node,edgeFrom,edgeTo)->node
),具有相同节点的所有三元组将归入同一组
然后,每个采集器生成对每个这样的三元组应用给定操作(sum
)的结果。我们在这里使用两个收集器,一个用于汇总在edgeFrom
中传输的项目,另一个用于edgeTo
中传输的项目。这两个收集器仅适用于共享相同节点的边,正如前面提到的密钥映射所保证的那样
作为旁注,在开发类似这样的非平凡约束时,我绝对建议使用对一些典型结果进行建模,并查看约束的行为是否符合预期。我将以前面答案中的代码作为我的出发点:
public Constraint minimizeShortage(ConstraintFactory constraintFactory) {
return constraintFactory.fromUnfiltered(Node.class)
.join(Edge.class,
equal(Node::getId, Edge::getFromId))
.groupBy((node, edgeFrom) -> node,
sum((node, tsaO) -> edgeFrom.transportedItems()))
.join(Edge.class,
equal((node, edge) -> node.getId(), Edge::getToId))
.groupBy((node, edgeFrom, edgeTo) -> node,
(node, edgeFrom, edgeTo) -> qtO,
sum((node, qtO, edgeTo) -> edgeTo.transportedItems()))
...
}
这段代码不太可能执行得很好,因为groupBy()
非常昂贵,将它们链接起来会使问题逐渐恶化。让我们看看是否可以只使用一个groupBy()
:
让我们再讨论一下。这个特殊的groupBy()
重载由两部分组成:键映射和收集器
键映射将三元组拆分为组-在这种情况下((node,edgeFrom,edgeTo)->node
),具有相同节点的所有三元组将归入同一组
然后,每个采集器生成对每个这样的三元组应用给定操作(sum
)的结果。我们在这里使用两个收集器,一个用于汇总在edgeFrom
中传输的项目,另一个用于edgeTo
中传输的项目。这两个收集器仅适用于共享相同节点的边,正如前面提到的密钥映射所保证的那样
作为旁注,在开发像这样的非平凡约束时,我绝对建议使用来模拟一些典型的结果,并查看约束的行为是否符合预期。即使这可以实现您希望的功能(我衷心建议您使用constraint Verifier()来验证这一点),它的性能也不会很好。单个约束中的多个groupby()是消除分数计算计数的好方法。我将很快提供一个不同的答案,尽管这可能会实现您希望它实现的功能(我衷心建议您使用Constraint Verifier()验证这一点),但它的性能不会很好。单个约束中的多个groupby()是消除分数计算计数的好方法。我很快会提供一个不同的答案。谢谢你的解释。你的建议正是我最初试图建立的。但是您的解决方案不起作用,并导致语法错误。无法解析“对象”中的方法“transportedItems()”。编译器不明白,edgeTo是从类EdgeI中发现的另一个故障,内部连接也是一个问题。之后第一次加入我会得到一个列表,如果电台是供应商(“nodeFrom”)。在节点中。我踢所有没有边且在nodeTo属性中有边的节点。在第二次加入之后,我得到了一个所有站点的列表,这些站点是供应商和另一个节点的需求者。所以我踢出了所有不是两者都踢的节点。我想做的是得到一个类似“Node,EdgeWhereNodeFrom,EdgeWhereNodeTo”的结果,其中Edge是所有可能的边的列表,EdgeWhereNodeFrom和EdgeWhereNodeTo是节点所在的边。这些流畅的API在编译器中有时会很棘手。如果您遇到类型错误,这通常是因为流中的其他内容出错,并且编译器无法从整体上解析fluent调用链。据我所知,我的代码示例没有任何问题,因此我猜测在写“…”的部分中有一些错误,我们在这里看不到。至于您的第二条与连接相关的评论,我建议我们