我在計劃員工輪班時遇到問題,其中員工在輪班中均勻(隨機)分布。
在我的最小示例中,我使用 Spring boot、Lombock 和 Optaplanner spring boot starter (8.15.0.Final) 包。
我在一個檔案中的最小示例:
package com.example.planner;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.optaplanner.core.api.domain.entity.PlanningEntity;
import org.optaplanner.core.api.domain.lookup.PlanningId;
import org.optaplanner.core.api.domain.solution.PlanningEntityCollectionProperty;
import org.optaplanner.core.api.domain.solution.PlanningScore;
import org.optaplanner.core.api.domain.solution.PlanningSolution;
import org.optaplanner.core.api.domain.solution.ProblemFactCollectionProperty;
import org.optaplanner.core.api.domain.valuerange.ValueRangeProvider;
import org.optaplanner.core.api.domain.variable.PlanningVariable;
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;
import org.optaplanner.core.api.solver.SolverManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import java.util.ArrayList;
import java.util.List;
@SpringBootApplication
public class PlannerApplication implements CommandLineRunner {
@Autowired
private SolverManager<Problem, Long> solverManager;
public static void main(String[] args) {
SpringApplication.run(PlannerApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
final var problem = new Problem(
List.of(new Employee(1L), new Employee(2L), new Employee(3L)),
List.of(new Shift(1L), new Shift(2L), new Shift(3L), new Shift(4L), new Shift(5L), new Shift(6L))
);
final var job = solverManager.solveAndListen(1L, id -> problem, bestSolution -> {
for (final var shift : bestSolution.shifts) {
System.err.println("Shift " shift.id ": Employee " shift.employee.id);
}
});
}
@NoArgsConstructor
public static class PlannerConstraintProvider implements ConstraintProvider {
@Override
public Constraint[] defineConstraints(ConstraintFactory constraintFactory) {
return new Constraint[]{};
}
}
@PlanningSolution
@Data @NoArgsConstructor @AllArgsConstructor
public static class Problem {
@ValueRangeProvider(id = "employeeRange")
@ProblemFactCollectionProperty
private List<Employee> employees;
@PlanningEntityCollectionProperty
private List<Shift> shifts = new ArrayList<>(0);
@PlanningScore
private HardSoftScore score;
public Problem(List<Employee> employees, List<Shift> shifts) {
this.employees = employees;
this.shifts = shifts;
}
}
@Data @NoArgsConstructor @AllArgsConstructor
public static class Employee {
@PlanningId
private Long id;
}
@PlanningEntity
@Data @NoArgsConstructor @AllArgsConstructor
public static class Shift {
@PlanningId
private Long id;
@PlanningVariable(valueRangeProviderRefs = "employeeRange")
private Employee employee;
public Shift(Long id) {
this.id = id;
}
}
}
這個例子的輸出是:
Shift 1: Employee 1
Shift 2: Employee 1
Shift 3: Employee 1
Shift 4: Employee 1
Shift 5: Employee 1
Shift 6: Employee 1
期望的輸出是:
Shift 1: Employee 1
Shift 2: Employee 2
Shift 3: Employee 3
Shift 4: Employee 1
Shift 5: Employee 2
Shift 6: Employee 3
(或其他統一組合)
uj5u.com熱心網友回復:
您沒有定義任何約束,因此 OptaPlaner 沒有理由提出更好的解決方案。你沒有告訴它什么更好。
OptaPlanner“認為”這個解決方案是最好的,因為(我猜)它的分數是0hard/0soft(你可以在控制臺中查看),這是一個理想的分數。
為了達到預期的輸出,您應該定義一個公平的作業量分配約束,該約束將按其作業量的平方懲罰每個員工。可能這樣的事情應該適用于您的情況:
@Override
public Constraint[] defineConstraints(ConstraintFactory constraintFactory) {
return new Constraint[] {
fairWorkloadDistribution(constraintFactory)
};
}
...
Constraint fairWorkloadDistribution(ConstraintFactory constraintFactory) {
return constraintFactory.forEach(Shift.class)
.groupBy(Shift::getEmployee, ConstraintCollectors.count())
.penalize(
"Employee workload squared",
HardSoftScore.ONE_SOFT,
(employee, shifts) -> shifts * shifts);
}
uj5u.com熱心網友回復:
你沒有限制。您還沒有告訴 OptaPlanner 要優化什么,因此所有解決方案都同樣有利于 OptaPlanner。
(事實上??,我很驚訝這段代碼沒有失敗。沒有約束的情況應該拋出例外。)
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/422672.html
標籤:
