时间表的平均分配

如何解决时间表的平均分配

我在 optaplanner 中有一个系统,可以为员工生成轮班。当有 e.x 6 名员工且轮班容量为 2 时,仅为 2 名员工生成解决方案,直到他们达到最大工作时间。我如何添加约束以便解决方案由混合员工生成

以下是 optaplanner 中定义的规则:

global HardMediumSoftLongscoreHolder scoreHolder;

// Hard constraints


rule "required skill for a shift"
    when
        $shift: Shift(employee != null,hasrequiredSkills() == false)
    then
        scoreHolder.penalize(kcontext,$shift.getLengthInMinutes());
end

rule "Unavailable time slot for an employee"
    when
        $availability: EmployeeAvailability(
                state == EmployeeAvailabilityState.UNAVAILABLE,$e : employee,$startDateTime : startDateTime,$endDateTime : endDateTime)
        Shift(employee == $e,$startDateTime < endDateTime,$endDateTime > startDateTime)
    then
        scoreHolder.penalize(kcontext,$availability.getDuration().toMinutes());
end

rule "No overlapping shifts"
    when
        $s : Shift(employee != null,$firstStartDateTime: startDateTime,$firstEndDateTime : endDateTime)
        $s2: Shift(employee == $e,this != $s,$firstStartDateTime < endDateTime,$firstEndDateTime > startDateTime)
    then
        scoreHolder.penalize(kcontext,$s2.getLengthInMinutes());
end

rule "No more than 2 consecutive shifts"
    when
        $s : Shift(
                employee != null,$firstEndDateTime : endDateTime)
        $s2: Shift(
                employee == $e,$firstEndDateTime == startDateTime,$secondEndDateTime : endDateTime)
        $s3: Shift(
                employee == $e,$secondEndDateTime == startDateTime,this != $s2)
    then
        scoreHolder.penalize(kcontext,$s3.getLengthInMinutes());
end

rule "Break between non-consecutive shifts is at least 10 hours"
    when
        $s : Shift(
                employee != null,$leftEndDateTime : endDateTime)
        Shift(
                employee == $e,$leftEndDateTime < startDateTime,$leftEndDateTime.until(startDateTime,ChronoUnit.HOURS) < 10,$rightStartDateTime: startDateTime)
    then
        long breakLength = $leftEndDateTime.until($rightStartDateTime,ChronoUnit.MINUTES);
        scoreHolder.penalize(kcontext,(10 * 60) - breakLength);
end

rule "Daily minutes must not exceed contract maximum"
    when
        $employee : Employee($contract : contract,$contract.getMaximumMinutesPerDay() != null)
        $s : Shift(employee == $employee,$startDateTime : startDateTime)
        accumulate(
            $other : Shift(
                employee == $employee,$shiftStart : startDateTime,$shiftEnd : endDateTime,$shiftStart.toLocalDate().equals($startDateTime.toLocalDate())
            ),$shiftCount : count($other),$totalMinutes : sum(Duration.between($shiftStart,$shiftEnd).toMinutes())
        )
        Number(this > $contract.getMaximumMinutesPerDay()) from $totalMinutes
    then
        scoreHolder.penalize(kcontext,(((long)$totalMinutes) - $contract.getMaximumMinutesPerDay()) / $shiftCount);
end

rule "Weekly minutes must not exceed contract maximum"
    when
        $rosterConstraintConfiguration : RosterConstraintConfiguration()
        $employee : Employee($contract : contract,$contract.getMaximumMinutesPerWeek() != null)
        $s : Shift(employee == $employee,DateTimeUtils.sameWeek($rosterConstraintConfiguration.getWeekStartDay(),$shiftStart,$startDateTime)
            ),$shiftEnd).toMinutes())
        )
        Number(this > $contract.getMaximumMinutesPerWeek()) from $totalMinutes
    then
        scoreHolder.penalize(kcontext,(((long)$totalMinutes) - $contract.getMaximumMinutesPerWeek()) / $shiftCount);
end

rule "Monthly minutes must not exceed contract maximum"
    when
        $employee : Employee($contract : contract,$contract.getMaximumMinutesPerMonth() != null)
        $s : Shift(employee == $employee,$shiftStart.getMonth() == $startDateTime.getMonth(),$shiftStart.getYear() == $startDateTime.getYear()
            ),$shiftEnd).toMinutes())
        )
        Number(this > $contract.getMaximumMinutesPerMonth()) from $totalMinutes
    then
        scoreHolder.penalize(kcontext,(((long)$totalMinutes) - $contract.getMaximumMinutesPerMonth()) / $shiftCount);
end

rule "Yearly minutes must not exceed contract maximum"
    when
        $employee : Employee($contract : contract,$contract.getMaximumMinutesPerYear() != null)
        $s : Shift(employee == $employee,$shiftEnd).toMinutes())
        )
        Number(this > $contract.getMaximumMinutesPerYear()) from $totalMinutes
    then
        scoreHolder.penalize(kcontext,(((long)$totalMinutes) - $contract.getMaximumMinutesPerYear()) / $shiftCount);
end


// Medium constraints


rule "Assign every shift"
    when
        Shift(employee == null)
    then
        scoreHolder.penalize(kcontext);
end

// Soft constraints


rule "Employee is not original employee"
    when
        $shift: Shift(originalEmployee != null,employee != null,employee != originalEmployee)
    then
        scoreHolder.penalize(kcontext,$shift.getLengthInMinutes());
end

rule "Undesired time slot for an employee"
    when
        $availability: EmployeeAvailability(
                state == EmployeeAvailabilityState.UNDESIRED,$availability.getDuration().toMinutes());
end

rule "Desired time slot for an employee"
    when
        $availability: EmployeeAvailability(
                state == EmployeeAvailabilityState.DESIRED,$endDateTime > startDateTime)
    then
        scoreHolder.reward(kcontext,$availability.getDuration().toMinutes());
end

rule "Employee is not rotation employee"
    when
        $shift: Shift(rotationEmployee != null,employee != rotationEmployee)
    then
        scoreHolder.penalize(kcontext,$shift.getLengthInMinutes());
end

解决方法

您应该在求解器配置文件中添加此配置

<localSearch>
  <unionMoveSelector>
    <changeMoveSelector>
      <selectionOrder>RANDOM</selectionOrder>
    </changeMoveSelector>
  </unionMoveSelector>
</localSearch>
,

可能是你的时间跨度太短了。假设它不是...

您应该制定一个对工作负载公平分配进行评分的约束。如果这 2 名员工完成所有工作的 100%,而其他 4 名员工完成所有工作的 0%,则存在“公平”问题。

举个例子,假设总共有 12 个工作单元要分配给这 6 名员工。即使这 12 个单位不超过年/月/周,甚至没有超过每日员工合同的约束或您制定的任何其他约束,那么额外的约束将确保 12 个工作单位将公平地分配到6 名员工,因此在理想情况下,他们每个人有 2 个工作单位。

现在,如果 2 名员工获得所有 12 个单位,则约束可能类似于:“对于每个员工,将他的努力与平均努力的差异平方,并对结果进行惩罚”。在上面提到的情况下,那将是

(6-2) squared + (6-2) squared + (0-2) squared + (0-2) squared + (0-2) squared + (0-2) squared 所以 16+16+4+4+4+4=48 个惩罚

在理想情况下,您有:

(2-2) squared + (2-2) squared + (2-2) squared + (2-2) squared + (2-2) squared + (2-2) squared 所以 0 个惩罚

如果你不考虑平方,你会:

(6-2) + (6-2) + (0-2) + (0-2) + (0-2) + (0-2) 所以 4+4-2-2-2-2 = 0 惩罚,这将完全违背约束的目标

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?