时间表的平均分配 [英] Equal distribution of Schedules

查看:98
本文介绍了时间表的平均分配的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在optaplanner中有一个系统,该系统可以为员工带来轮班.如果有6名员工,而轮班能力为2名,则仅为2名员工生成解决方案,直到他们达到最大工作时间为止.如何添加约束,以便由混合员工生成解决方案.

I have a system in optaplanner which generates shifts for employees. When there are for e.x 6 employees and shift capacity is two, the solution is generated only for 2 employees until they reach the maximum working hours. How can I add a constraint so that the solution is generated with mixed employees.

以下是optaplanner中定义的规则:

Below are rules that are defined in 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, $e : employee, $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,
                $e : employee,
                $firstEndDateTime : endDateTime)
        $s2: Shift(
                employee == $e,
                $firstEndDateTime == startDateTime,
                this != $s,
                $secondEndDateTime : endDateTime)
        $s3: Shift(
                employee == $e,
                $secondEndDateTime == startDateTime,
                this != $s,
                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,
                $e : employee,
                $leftEndDateTime : endDateTime)
        Shift(
                employee == $e,
                $leftEndDateTime < startDateTime,
                $leftEndDateTime.until(startDateTime, ChronoUnit.HOURS) < 10,
                this != $s,
                $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, $startDateTime : startDateTime)
        accumulate(
            $other : Shift(
                employee == $employee, $shiftStart : startDateTime,
                $shiftEnd : endDateTime,
                DateTimeUtils.sameWeek($rosterConstraintConfiguration.getWeekStartDay(), $shiftStart, $startDateTime)
            ),
            $shiftCount : count($other),
            $totalMinutes : sum(Duration.between($shiftStart, $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, $startDateTime : startDateTime)
        accumulate(
            $other : Shift(
                employee == $employee, $shiftStart : startDateTime,
                $shiftEnd : endDateTime,
                $shiftStart.getMonth() == $startDateTime.getMonth(),
                $shiftStart.getYear() == $startDateTime.getYear()
            ),
            $shiftCount : count($other),
            $totalMinutes : sum(Duration.between($shiftStart, $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, $startDateTime : startDateTime)
        accumulate(
            $other : Shift(
                employee == $employee, $shiftStart : startDateTime,
                $shiftEnd : endDateTime,
                $shiftStart.getYear() == $startDateTime.getYear()
            ),
            $shiftCount : count($other),
            $totalMinutes : sum(Duration.between($shiftStart, $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,
                $e : employee,
                $startDateTime : startDateTime,
                $endDateTime : endDateTime)
        Shift(employee == $e,
                $startDateTime < endDateTime,
                $endDateTime > startDateTime)
    then
        scoreHolder.penalize(kcontext, $availability.getDuration().toMinutes());
end

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

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

推荐答案

您应将此配置添加到求解器配置文件中

You should add this configuration in solver config file

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

这篇关于时间表的平均分配的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆