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
        }
    }
}
posted @   南翔技校毕业后  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 提示词工程——AI应用必不可少的技术
· 字符编码:从基础到乱码解决
· 地球OL攻略 —— 某应届生求职总结
点击右上角即可分享
微信分享提示