optaplanner应用
问题描述
假如生产过程计划生产一批化学油品,需要将这些油品分配到一些反应釜进行生产制造,反应釜是满负载运作。那么需满足以下硬约束:
- 至少满足生产总量
- 不能过多生产,导致浪费
- 平均使用反应釜
业务建模
代码示例
规划实体类
@Data
@PlanningEntity
public class ReactorUsage {
@PlanningId
private Long id;
private int reactorCapacity; // 反应釜容量
private Integer targetProduction; // 生产目标总量
@PlanningVariable(valueRangeProviderRefs = "usageCountRange")
private Integer usageCount; // 反应釜使用次数
public ReactorUsage() {
}
public ReactorUsage(Long id, int reactorCapacity, int targetProduction) {
this.id = id;
this.reactorCapacity = reactorCapacity;
this.targetProduction = targetProduction;
}
public int getProductionAmount() {
return reactorCapacity * (usageCount == null ? 0 : usageCount);
}
}
规划解决方案类
@Data
@PlanningSolution
public class ProductionSchedulingSolution {
@PlanningEntityCollectionProperty
private List<ReactorUsage> reactorUsages;
@PlanningScore
private HardSoftScore score;
public ProductionSchedulingSolution() {
}
public int getTotalProduction() {
int total = 0;
for (ReactorUsage usage : reactorUsages) {
total += usage.getProductionAmount();
}
return total;
}
@ValueRangeProvider(id = "usageCountRange")
public ValueRange<Integer> getUsageCountRange() {
// 假设 usageCount 的取值范围是 0 到 11
return new IntValueRange(0, 11);
}
}
评分规则类
public class ProductionConstraintProvider implements ConstraintProvider {
@Override
public Constraint[] defineConstraints(ConstraintFactory constraintFactory) {
return new Constraint[]{
atLeastProduction(constraintFactory),
minDev(constraintFactory),
averageRounds(constraintFactory)};
}
private Constraint atLeastProduction(ConstraintFactory constraintFactory) {
// 至少满足生产量
UniConstraintStream<ReactorUsage> stream = constraintFactory.from(ReactorUsage.class);
BiConstraintStream<Integer, Integer> groupByStream = stream.groupBy(
new Function<ReactorUsage, Integer>() {
@Override
public Integer apply(ReactorUsage reactorUsage) {
return reactorUsage.getTargetProduction();
}
},
ConstraintCollectors.sum(ReactorUsage::getProductionAmount)
);
BiConstraintStream<Integer, Integer> filterStream = groupByStream.filter(
new BiPredicate<Integer, Integer>() {
@Override
public boolean test(Integer target, Integer sum) {
return sum < target;
}
});
return filterStream.penalize(HardSoftScore.ONE_HARD).asConstraint("at least");
}
private Constraint minDev(ConstraintFactory constraintFactory) {
// 最小差
UniConstraintStream<ReactorUsage> stream = constraintFactory.from(ReactorUsage.class);
BiConstraintStream<Integer, Integer> groupByStream = stream.groupBy(
new Function<ReactorUsage, Integer>() {
@Override
public Integer apply(ReactorUsage reactorUsage) {
return reactorUsage.getTargetProduction();
}
},
ConstraintCollectors.sum(ReactorUsage::getProductionAmount)
);
QuadConstraintStream<Integer, Integer, ReactorUsage, ReactorUsage> joinStream = groupByStream.join(ReactorUsage.class).join(ReactorUsage.class);
QuadConstraintStream<Integer, Integer, ReactorUsage, ReactorUsage> filterStream = joinStream.filter(new QuadPredicate<Integer, Integer, ReactorUsage, ReactorUsage>() {
@Override
public boolean test(Integer target, Integer sum, ReactorUsage r1, ReactorUsage r2) {
return (sum - target) > Math.abs(r2.getReactorCapacity() - r1.getReactorCapacity());
}
});
return filterStream.penalize(HardSoftScore.ONE_HARD).asConstraint("min dev");
}
private Constraint averageRounds(ConstraintFactory constraintFactory) {
// 平均使用容器
BiConstraintStream<ReactorUsage, ReactorUsage> stream = constraintFactory.forEachUniquePair(ReactorUsage.class);
BiConstraintStream<ReactorUsage, ReactorUsage> filterStream = stream.filter(new BiPredicate<ReactorUsage, ReactorUsage>() {
@Override
public boolean test(ReactorUsage r1, ReactorUsage r2) {
return Math.abs(r2.getUsageCount() - r1.getUsageCount()) > 2;
}
});
return filterStream.penalize(HardSoftScore.ONE_HARD).asConstraint("average rounds");
}
}
集成SpringBoot
config
@Configuration
public class LepsOptaPlannerConfig {
@Bean
public SolverConfig solverConfig() {
SolverConfig solverConfig = new SolverConfig();
solverConfig.withSolutionClass(ProductionSchedulingSolution.class)
.withEntityClasses(ReactorUsage.class)
.withConstraintProviderClass(ProductionConstraintProvider.class)
.withTerminationConfig(new TerminationConfig().withSecondsSpentLimit(10L));
return solverConfig;
}
@Bean
public SolverManager<ProductionSchedulingSolution, Long> LepsSolverManager(SolverConfig solverConfig) {
SolverFactory<ProductionSchedulingSolution> solverFactory = SolverFactory.create(solverConfig);
return SolverManager.create(solverFactory, new SolverManagerConfig());
}
}
service
@Service
public class ProductionSchedulingService {
@Autowired
private SolverManager<ProductionSchedulingSolution, Long> lepsSolverManager;
public ProductionSchedulingSolution solve(Integer target) {
// 初始化反应釜使用实体列表
List<ReactorUsage> reactorUsages = new ArrayList<>();
// 2 个 5t 反应釜
reactorUsages.add(new ReactorUsage(1L, 5,target));
reactorUsages.add(new ReactorUsage(2L, 5,target));
// 2 个 7t 反应釜
reactorUsages.add(new ReactorUsage(3L, 7,target));
reactorUsages.add(new ReactorUsage(4L, 7,target));
// 1 个 9t 反应釜
reactorUsages.add(new ReactorUsage(5L, 9,target));
// 创建初始解决方案
ProductionSchedulingSolution initialSolution = new ProductionSchedulingSolution();
initialSolution.setReactorUsages(reactorUsages);
// 提交求解任务
SolverJob<ProductionSchedulingSolution, Long> solverJob = lepsSolverManager.solve(1L, initialSolution);
try {
// 获取求解结果
return solverJob.getFinalBestSolution();
} catch (InterruptedException | ExecutionException e) {
Thread.currentThread().interrupt();
throw new IllegalStateException("Solving failed.", e);
}
}
}
controller
@RestController
@RequestMapping("/leps")
public class ProductionSchedulingController {
@Autowired
private ProductionSchedulingService productionSchedulingService;
@GetMapping("/schedule")
public Map<String, Object> getProductionSchedule(@RequestParam("target") Integer target) {
ProductionSchedulingSolution solution = productionSchedulingService.solve(target);
Map<String, Object> result = new HashMap<>();
result.put("solution", solution);
return result;
}
}
展示运行结果
发送请求
结果数据
{
"solution": {
"reactorUsages": [
{
"id": 1,
"reactorCapacity": 5,
"targetProduction": 85,
"usageCount": 4,
"productionAmount": 20
},
{
"id": 2,
"reactorCapacity": 5,
"targetProduction": 85,
"usageCount": 2,
"productionAmount": 10
},
{
"id": 3,
"reactorCapacity": 7,
"targetProduction": 85,
"usageCount": 2,
"productionAmount": 14
},
{
"id": 4,
"reactorCapacity": 7,
"targetProduction": 85,
"usageCount": 2,
"productionAmount": 14
},
{
"id": 5,
"reactorCapacity": 9,
"targetProduction": 85,
"usageCount": 3,
"productionAmount": 27
}
],
"score": {
"initScore": 0,
"hardScore": 0,
"softScore": 0,
"feasible": true,
"solutionInitialized": true,
"zero": true
},
"totalProduction": 85,
"usageCountRange": {
"size": 11,
"empty": false
}
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 提示词工程——AI应用必不可少的技术
· 字符编码:从基础到乱码解决
· 地球OL攻略 —— 某应届生求职总结