如何合并两个ConstraintStream以实现类似SQL UNION ALL的效果?
目前OptaPlanner的ConstraintStream API 8.28.0.Final及以上版本已提供原生的concat()操作,等价于SQL的UNION ALL,可直接实现多个同类型约束流的合并。如果使用旧版本,也可以通过影子变量方案实现需求。
方案1:使用原生concat方法(推荐,适用于8.28.0及以上版本)
你需要先将已购买牌照的频谱使用权展开为单个元素流,再和现有频谱权流直接合并:
private Constraint computeAggregatedSpectrum(ConstraintFactory factory) { // 现有频谱权流:从问题事实集合直接获取所有已有权限 UniConstraintStream<SpectrumRight> existingRightStream = factory.from(SpectrumRight.class); // 已购买牌照的频谱权流:筛选拍下的牌照,展开为单个频谱权元素 UniConstraintStream<SpectrumRight> purchasedRightStream = factory.from(AuctionLicense.class) .filter(AuctionLicense::isBuy) // 用flattenLast展开频谱权列表,替代原有的map操作 .flattenLast(AuctionLicense::getSpectrumRights); // concat等价于UNION ALL,直接合并两个同类型流 return existingRightStream .concat(purchasedRightStream) .groupBy(toSortedSet()) .map(MetricsCalculator::computeAggregatedSpectrumMhzPops) .reward("聚合频谱指标计算", HardSoftScore.ONE_SOFT); }
如果需要去重,在concat后追加.groupBy(Function.identity(), countDistinct())或者使用distinct()转换器即可。
提示:如果使用
toSortedSet(),需要确保SpectrumRight类实现了Comparable接口,或者传入自定义的比较器。
方案2:影子变量预计算(兼容所有版本,适合复杂合并逻辑)
如果使用旧版本OptaPlanner,或者合并逻辑后续可能扩展,可以通过影子变量预计算合并后的频谱权集合:
第一步:实现变量监听器
监听牌照购买状态的变化,自动更新合并后的频谱权集合:
public class MergedSpectrumRightListener implements VariableListener<AuctionLicense> { @Override public void beforeVariableChanged(ScoreDirector<?> scoreDirector, AuctionLicense entity) {} @Override public void afterVariableChanged(ScoreDirector<?> scoreDirector, AuctionLicense entity) { SpectrumSolution solution = (SpectrumSolution) scoreDirector.getWorkingSolution(); SortedSet<SpectrumRight> mergedSet = new TreeSet<>(solution.getExistingPortfolio()); for (AuctionLicense license : solution.getAuctionLicenses()) { if (license.isBuy()) { mergedSet.addAll(license.getSpectrumRights()); } } // 通知分数计算器集合发生变更 scoreDirector.beforeProblemPropertyChanged(solution); solution.setMergedSpectrumRights(mergedSet); scoreDirector.afterProblemPropertyChanged(solution); } @Override public void resetWorkingSolution(ScoreDirector<?> scoreDirector) {} @Override public void close() {} }
第二步:在规划解类中配置影子变量
@PlanningSolution public class SpectrumSolution { @ProblemFactCollectionProperty private List<SpectrumRight> existingPortfolio; @PlanningEntityCollectionProperty private List<AuctionLicense> auctionLicenses; // 配置影子变量,绑定监听器和触发变量 @ShadowVariable( variableListenerClass = MergedSpectrumRightListener.class, sourceEntityClass = AuctionLicense.class, sourceVariableName = "buy" ) private SortedSet<SpectrumRight> mergedSpectrumRights; // 省略其他属性和getter/setter }
第三步:编写约束逻辑
直接从预计算的合并集合构造流即可:
private Constraint computeAggregatedSpectrum(ConstraintFactory factory) { return factory.from(SpectrumSolution.class) .map(SpectrumSolution::getMergedSpectrumRights) .map(MetricsCalculator::computeAggregatedSpectrumMhzPops) .reward("聚合频谱指标计算", HardSoftScore.ONE_SOFT); }
内容的提问来源于stack exchange,提问作者TheDarkSaint
相关产品推荐
相关产品推荐

