You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

OptaPlanner任务分配项目规则构建咨询:如何基于Solution类实现工人任务位置邻近性软约束

嘿,这个问题我刚好有经验!之前做工人任务分配项目的时候,也用OptaPlanner处理过类似的连续任务位置约束,给你分享一下具体的实现思路和示例吧。

一、在DRL规则中获取工人的上一个任务信息

首先,你需要确保你的Solution类(比如命名为TaskAssignmentSolution)中已经维护了工人的任务历史数据——比如一个Map<Worker, Task>用来存储每个工人的最后完成任务,或者一个Map<Worker, List<Task>>存储工人按执行顺序排列的任务列表。假设你的Solution类有这样一个方法:

// TaskAssignmentSolution.java
public Task getLastCompletedTask(Worker worker) {
    // 从Solution的存储结构中返回工人的上一个已完成任务,没有则返回null
    return workerLastTaskMap.getOrDefault(worker, null);
}

接下来在DRL规则文件中,你可以直接匹配当前的Solution实例(OptaPlanner会自动将working solution暴露给规则引擎),然后调用它的方法获取上一个任务:

// taskAssignmentRules.drl
package com.yourcompany.taskassignment.rules;

import com.yourcompany.taskassignment.domain.Worker;
import com.yourcompany.taskassignment.domain.Task;
import com.yourcompany.taskassignment.solution.TaskAssignmentSolution;
import org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScoreHolder;

global HardSoftScoreHolder scoreHolder;

rule "MinimizeTravelDistanceBetweenConsecutiveTasks"
    when
        // 匹配一个工人
        $worker : Worker()
        // 匹配该工人当前分配的任务(假设任务有ASSIGNED状态标识待执行)
        $currentTask : Task(worker == $worker, taskStatus == TaskStatus.ASSIGNED)
        // 获取当前的解决方案实例
        $solution : TaskAssignmentSolution()
        // 通过Solution获取该工人的上一个已完成任务(排除null的情况)
        $lastTask : Task(this == $solution.getLastCompletedTask($worker))
    then
        // 计算两个任务地点的距离,距离越大,软约束惩罚越高(负分)
        int distance = calculateDistance($currentTask.getLocation(), $lastTask.getLocation());
        scoreHolder.addSoftConstraintMatch(kcontext, -distance);
end

// 辅助方法:计算两个地点的距离(这里用曼哈顿距离示例)
function int calculateDistance(Location a, Location b) {
    return Math.abs(a.getX() - b.getX()) + Math.abs(a.getY() - b.getY());
}
二、OptaPlanner参考项目推荐

OptaPlanner官方提供的示例项目里,有几个非常适合你的场景参考:

  • Vehicle Routing Problem (VRP):这个示例核心是优化车辆的路径,让车辆连续任务之间的行驶距离最小化——和你的工人连续任务位置约束逻辑几乎一致,只是主体从车辆换成了工人。你可以学习它如何维护任务序列、计算连续任务的距离,以及在规则中访问全局解决方案数据。
  • Employee Rostering:这个示例专注于员工排班,虽然主要处理时间冲突、技能匹配等约束,但它展示了如何在规则中访问Solution里的员工历史任务数据,对你的场景有借鉴意义。

另外,如果你觉得DRL规则不够直观,也可以尝试用Java代码实现约束(OptaPlanner的ConstraintProvider接口),这样类型更安全,处理复杂的Solution数据也更方便:

// TaskAssignmentConstraintProvider.java
package com.yourcompany.taskassignment.constraints;

import com.yourcompany.taskassignment.domain.Task;
import com.yourcompany.taskassignment.domain.Worker;
import com.yourcompany.taskassignment.solution.TaskAssignmentSolution;
import org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScore;
import org.optaplanner.core.api.score.stream.Constraint;
import org.optaplanner.core.api.score.stream.ConstraintFactory;
import org.optaplanner.core.api.score.stream.ConstraintProvider;

public class TaskAssignmentConstraintProvider implements ConstraintProvider {
    @Override
    public Constraint[] defineConstraints(ConstraintFactory constraintFactory) {
        return new Constraint[]{
                minimizeTravelBetweenConsecutiveTasks(constraintFactory)
        };
    }

    private Constraint minimizeTravelBetweenConsecutiveTasks(ConstraintFactory constraintFactory) {
        return constraintFactory.from(Task.class)
                // 关联任务对应的工人
                .join(Worker.class, equal(Task::getWorker))
                // 过滤出有上一个任务的情况
                .filter((task, worker) -> {
                    TaskAssignmentSolution solution = constraintFactory.getSolution();
                    return solution.getLastCompletedTask(worker) != null;
                })
                // 按距离惩罚软约束
                .penalize("Minimize travel distance", HardSoftScore.ONE_SOFT,
                        (task, worker) -> {
                            TaskAssignmentSolution solution = constraintFactory.getSolution();
                            Task lastTask = solution.getLastCompletedTask(worker);
                            return calculateDistance(task.getLocation(), lastTask.getLocation());
                        });
    }

    private int calculateDistance(Location a, Location b) {
        return Math.abs(a.getX() - b.getX()) + Math.abs(a.getY() - b.getY());
    }
}
三、额外注意事项
  • 确保Solution中的任务历史数据是线程安全的:如果启用了OptaPlanner的多线程求解,Solution会被多个线程访问,建议用ConcurrentHashMap这类线程安全的集合来存储工人的任务信息。
  • 明确任务的执行顺序:如果你的任务有时间先后要求,需要在Solution中维护任务的执行顺序(比如给Task加executionOrder字段或时间戳),这样才能准确找到“上一个”任务。
  • 测试约束逻辑:可以用OptaPlanner的调试工具查看约束匹配情况,确保规则正确触发并计算了预期的惩罚值。

内容的提问来源于stack exchange,提问作者brewphone

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 16:39:05