From 466ccdd681b6e66f38bc7bc8c1c4e7b7e6660297 Mon Sep 17 00:00:00 2001 From: "Francois Berube\\frabe" Date: Mon, 2 Apr 2018 22:51:19 -0400 Subject: [PATCH 1/3] Creation des bases de l'algorithmes de recouvrement des horaires avec absences. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TODO : Il reste a trouver le recouvrement qui minimise le cout des employes. Cela nécessite de trouver le coût de l'horaire optimal. --- .../AbsenceSchedulesArray.java | 58 +++++-- .../AbsenceSchedules/AbsencesVector.java | 16 +- .../ParametersInitialSchedules.java | 20 ++- Travail_de_session/MainClass.java | 2 +- .../ScheduleUtil/SchedulesArray.java | 138 +++++++++++----- .../RecoveredSchedulesArray.java | 154 +++++++++++++++++- 6 files changed, 318 insertions(+), 70 deletions(-) diff --git a/Travail_de_session/AbsenceSchedules/AbsenceSchedulesArray.java b/Travail_de_session/AbsenceSchedules/AbsenceSchedulesArray.java index 3e1f5f6..75bb4ce 100644 --- a/Travail_de_session/AbsenceSchedules/AbsenceSchedulesArray.java +++ b/Travail_de_session/AbsenceSchedules/AbsenceSchedulesArray.java @@ -6,27 +6,57 @@ import jdistlib.rng.RandomEngine; import org.chocosolver.solver.Solution; public class AbsenceSchedulesArray extends SchedulesArray{ - + + protected boolean[][] partTimeEmployeeUnavailabilitySchedules; + protected boolean[][] fullTimeEmployeeUnavailabilitySchedules; + public AbsenceSchedulesArray(SchedulesArray myScheduleArray) { super(myScheduleArray); } - public void generateAbsences(RandomEngine r) { - for (int i = 0; i < this.maxPartTimeEmployee; i++) { - int[] a = new AbsencesVector(this.workPeriodsPerSchedule, r).getAbsencesVector(); - for (int j = 0; j < this.workPeriodsPerSchedule; j++) { - this.PartTimeSchedules[i][j] = this.PartTimeSchedules[i][j] * a[j]; + public AbsenceSchedulesArray(AbsenceSchedulesArray myAbsenceScheduleArray) { + super(myAbsenceScheduleArray); + this.partTimeEmployeeUnavailabilitySchedules = new boolean[this.maxPartTimeEmployee][this.workPeriodsPerSchedule]; + for (int workPeriod = 0; workPeriod < this.workPeriodsPerSchedule; workPeriod++) { + for (int employee = 0; employee < this.maxPartTimeEmployee; employee++) { + this.partTimeEmployeeUnavailabilitySchedules[employee][workPeriod] = myAbsenceScheduleArray.partTimeEmployeeUnavailabilitySchedules[employee][workPeriod]; } } - for (int i = 0; i < this.maxFullTimeEmployee; i++) { - int[] a = new AbsencesVector(this.workPeriodsPerSchedule, r).getAbsencesVector(); - for (int j = 0; j < this.workPeriodsPerSchedule; j++) { - this.FullTimeSchedules[i][j] = this.FullTimeSchedules[i][j] * a[j]; + this.fullTimeEmployeeUnavailabilitySchedules = new boolean[this.maxFullTimeEmployee][this.workPeriodsPerSchedule]; + for (int workPeriod = 0; workPeriod < this.workPeriodsPerSchedule; workPeriod++) { + for (int employee = 0; employee < this.maxFullTimeEmployee; employee++) { + this.fullTimeEmployeeUnavailabilitySchedules[employee][workPeriod] = myAbsenceScheduleArray.fullTimeEmployeeUnavailabilitySchedules[employee][workPeriod]; } } - updateEmployeesPerWorkPeriod(); - updateWorkingPeriodsPerPartTimeEmployees(); - updateWorkingPeriodsPerFullTimeEmployees(); } - + + public void generateAbsences(RandomEngine r) { + this.partTimeEmployeeUnavailabilitySchedules = new boolean[this.maxPartTimeEmployee][this.workPeriodsPerSchedule]; + for (int i = 0; i < this.maxPartTimeEmployee; i++) { + boolean[] a = new AbsencesVector(this.workPeriodsPerSchedule, r).getAbsencesVector(); + for (int j = 0; j < this.workPeriodsPerSchedule; j++) { + this.partTimeSchedules[i][j] = this.initialPartTimeSchedules[i][j] && a[j]; + } + } + this.fullTimeEmployeeUnavailabilitySchedules = new boolean[this.maxFullTimeEmployee][this.workPeriodsPerSchedule]; + for (int i = 0; i < this.maxFullTimeEmployee; i++) { + boolean[] a = new AbsencesVector(this.workPeriodsPerSchedule, r).getAbsencesVector(); + for (int j = 0; j < this.workPeriodsPerSchedule; j++) { + this.fullTimeSchedules[i][j] = this.fullTimeSchedules[i][j] && a[j]; + } + } + updateAllEmployeesPerWorkPeriod(); + updateAllWorkingPeriodsPerPartTimeEmployees(); + updateAllWorkingPeriodsPerFullTimeEmployees(); + } + + + protected boolean isPartTimeEmployeeAbsent(int employee, int workPeriod) { + return this.partTimeSchedules[employee][workPeriod] != this.initialPartTimeSchedules[employee][workPeriod]; + } + + protected boolean isFullTimeEmployeeAbsent(int employee, int workPeriod) { + return this.fullTimeSchedules[employee][workPeriod] != this.initialFullTimeSchedules[employee][workPeriod]; + } + } \ No newline at end of file diff --git a/Travail_de_session/AbsenceSchedules/AbsencesVector.java b/Travail_de_session/AbsenceSchedules/AbsencesVector.java index e285198..46351e9 100644 --- a/Travail_de_session/AbsenceSchedules/AbsencesVector.java +++ b/Travail_de_session/AbsenceSchedules/AbsencesVector.java @@ -4,30 +4,30 @@ import jdistlib.Binomial; import jdistlib.rng.RandomEngine; public class AbsencesVector { - private int[] AbsencesVector; - private double probAbsence = 0.02; - private double probReturn = 0.80; + private boolean[] AbsencesVector; + private double probPresence = 0.90; + private double probReturn = 0.50; public AbsencesVector(int length, RandomEngine r) { int current = 1; - AbsencesVector = new int[length]; - Binomial b1 = new Binomial(1, probAbsence); + AbsencesVector = new boolean[length]; + Binomial b1 = new Binomial(1, probPresence); b1.setRandomEngine(r); Binomial b2 = new Binomial(1, probReturn); b2.setRandomEngine(r); for (int i = 0; i < length; i++) { if (current == 1) { current = (int) b1.random(); - AbsencesVector[i] = current; + AbsencesVector[i] = current == 1; } else { current = (int) b2.random(); - AbsencesVector[i] = current; + AbsencesVector[i] = current == 1; } } } - public int[] getAbsencesVector() { + public boolean[] getAbsencesVector() { return AbsencesVector; } } diff --git a/Travail_de_session/InitialSchedules/ParametersInitialSchedules.java b/Travail_de_session/InitialSchedules/ParametersInitialSchedules.java index 1b35e1f..2208548 100644 --- a/Travail_de_session/InitialSchedules/ParametersInitialSchedules.java +++ b/Travail_de_session/InitialSchedules/ParametersInitialSchedules.java @@ -16,7 +16,9 @@ public class ParametersInitialSchedules { int minWorkingHoursOfPartTimeEmployeesPerSchedule; int maxWorkingHoursOfPartTimeEmployeesPerSchedule; int minWorkingPeriodsOfPartTimeEmployeesPerSchedule; - int maxWorkingPeriodsOfPartTimeEmployeesPerSchedule; + public int maxConsecutiveWorkingPeriodsOfPartTimeEmployeesPerShiftWork; + public int minConsecutiveNonWorkingPeriodsBetweenShiftWorksOfPartTimeEmployees; + public int maxWorkingPeriodsOfPartTimeEmployeesPerSchedule; int fixedCostOfFullTimeEmployeesPerSchedule; int regularHourlyRateOfFullTimeEmployees; @@ -24,7 +26,9 @@ public class ParametersInitialSchedules { int workingHoursOfFullTimeEmployeesPerSchedule; int maxWorkingHoursOfFullTimeEmployeesPerSchedule; int workingPeriodsOfFullTimeEmployeesPerSchedule; - int maxWorkingPeriodsOfFullTimeEmployeesPerSchedule; + public int maxConsecutiveWorkingPeriodsOfFullTimeEmployeesPerShiftWork; + public int minConsecutiveNonWorkingPeriodsBetweenShiftWorksOfFullTimeEmployees; + public int maxWorkingPeriodsOfFullTimeEmployeesPerSchedule; int workingHoursPaidAtRegularHourlyRatePerSchedule; @@ -57,6 +61,12 @@ public class ParametersInitialSchedules { this.hourlyRateOfPartTimeEmployees = 12; // To simulate lower productivity this.minWorkingHoursOfPartTimeEmployeesPerSchedule = 32; this.maxWorkingHoursOfPartTimeEmployeesPerSchedule = 64; + int maxConsecutiveWorkingHoursOfPartTimeEmployeesPerShiftWork = 12; + this.maxConsecutiveWorkingPeriodsOfPartTimeEmployeesPerShiftWork + = (int) (maxConsecutiveWorkingHoursOfPartTimeEmployeesPerShiftWork / this.hoursPerWorkPeriod); + int minConsecutiveNonWorkingHoursBetweenShiftWorksOfPartTimeEmployees = 12; + this.minConsecutiveNonWorkingPeriodsBetweenShiftWorksOfPartTimeEmployees + = (int) (minConsecutiveNonWorkingHoursBetweenShiftWorksOfPartTimeEmployees / this.hoursPerWorkPeriod); this.minWorkingPeriodsOfPartTimeEmployeesPerSchedule = (int) (this.minWorkingHoursOfPartTimeEmployeesPerSchedule / this.hoursPerWorkPeriod); this.maxWorkingPeriodsOfPartTimeEmployeesPerSchedule @@ -70,6 +80,12 @@ public class ParametersInitialSchedules { this.workingHoursOfFullTimeEmployeesPerSchedule = 80; this.maxWorkingHoursOfFullTimeEmployeesPerSchedule = 120; + int maxConsecutiveWorkingHoursOfFullTimeEmployeesPerShiftWork = 12; + this.maxConsecutiveWorkingPeriodsOfFullTimeEmployeesPerShiftWork + = (int) (maxConsecutiveWorkingHoursOfFullTimeEmployeesPerShiftWork / this.hoursPerWorkPeriod); + int minConsecutiveNonWorkingHoursBetweenShiftWorksOfFullTimeEmployees = 12; + this.minConsecutiveNonWorkingPeriodsBetweenShiftWorksOfFullTimeEmployees + = (int) (minConsecutiveNonWorkingHoursBetweenShiftWorksOfFullTimeEmployees / this.hoursPerWorkPeriod); this.workingPeriodsOfFullTimeEmployeesPerSchedule = (int) (this.workingHoursOfFullTimeEmployeesPerSchedule / this.hoursPerWorkPeriod); this.maxWorkingPeriodsOfFullTimeEmployeesPerSchedule diff --git a/Travail_de_session/MainClass.java b/Travail_de_session/MainClass.java index 53ef6db..4970690 100644 --- a/Travail_de_session/MainClass.java +++ b/Travail_de_session/MainClass.java @@ -94,7 +94,7 @@ public class MainClass { List recoveredSchedulesArrayList = new ArrayList<>(); for (SchedulesArray absenceSchedule : absenceSchedulesArrayList) { - RecoveredSchedulesArray recoveredSchedule = new RecoveredSchedulesArray(absenceSchedule); + RecoveredSchedulesArray recoveredSchedule = new RecoveredSchedulesArray((AbsenceSchedulesArray) absenceSchedule); recoveredSchedule.recoverAbsenceScheduleOptimally(); recoveredSchedulesArrayList.add(recoveredSchedule); } diff --git a/Travail_de_session/ScheduleUtil/SchedulesArray.java b/Travail_de_session/ScheduleUtil/SchedulesArray.java index f62217a..b21b14a 100644 --- a/Travail_de_session/ScheduleUtil/SchedulesArray.java +++ b/Travail_de_session/ScheduleUtil/SchedulesArray.java @@ -17,11 +17,15 @@ import org.chocosolver.solver.Solution; */ public class SchedulesArray { - protected int[][] PartTimeSchedules; - protected int[][] FullTimeSchedules; + protected boolean[][] initialPartTimeSchedules; + protected boolean[][] initialFullTimeSchedules; + protected boolean[][] partTimeSchedules; + protected boolean[][] fullTimeSchedules; protected int[] employeesPerWorkPeriod; protected int[] workingPeriodsPerPartTimeEmployees; protected int[] workingPeriodsPerFullTimeEmployees; + protected boolean[] isPartTimeEmployeeActive; + protected boolean[] isFullTimeEmployeeActive; protected int maxPartTimeEmployee; protected int maxFullTimeEmployee; protected int workPeriodsPerSchedule; @@ -43,17 +47,31 @@ public class SchedulesArray { this.daysPerSchedule = myScheduleArray.daysPerSchedule; this.myModelInitialSchedules = myScheduleArray.myModelInitialSchedules; - this.PartTimeSchedules = new int[this.maxPartTimeEmployee][this.workPeriodsPerSchedule]; - this.FullTimeSchedules = new int[this.maxFullTimeEmployee][this.workPeriodsPerSchedule]; + this.isPartTimeEmployeeActive = new boolean[this.maxPartTimeEmployee]; + for (int employee = 0; employee < this.maxPartTimeEmployee; employee++) { + this.isPartTimeEmployeeActive[employee] = myScheduleArray.isPartTimeEmployeeActive[employee]; + } + + this.isFullTimeEmployeeActive = new boolean[this.maxFullTimeEmployee]; + for (int employee = 0; employee < this.maxFullTimeEmployee; employee++) { + this.isFullTimeEmployeeActive[employee] = myScheduleArray.isFullTimeEmployeeActive[employee]; + } + + this.partTimeSchedules = new boolean[this.maxPartTimeEmployee][this.workPeriodsPerSchedule]; + this.fullTimeSchedules = new boolean[this.maxFullTimeEmployee][this.workPeriodsPerSchedule]; + this.initialPartTimeSchedules = new boolean[this.maxPartTimeEmployee][this.workPeriodsPerSchedule]; + this.initialFullTimeSchedules = new boolean[this.maxFullTimeEmployee][this.workPeriodsPerSchedule]; for (int workPeriod = 0; workPeriod < this.workPeriodsPerSchedule; workPeriod++) { for (int employee = 0; employee < this.maxPartTimeEmployee; employee++) { - this.PartTimeSchedules[employee][workPeriod] = myScheduleArray.PartTimeSchedules[employee][workPeriod]; + this.partTimeSchedules[employee][workPeriod] = myScheduleArray.partTimeSchedules[employee][workPeriod]; + this.initialPartTimeSchedules[employee][workPeriod] = myScheduleArray.initialPartTimeSchedules[employee][workPeriod]; } } for (int workPeriod = 0; workPeriod < this.workPeriodsPerSchedule; workPeriod++) { for (int employee = 0; employee < this.maxFullTimeEmployee; employee++) { - this.FullTimeSchedules[employee][workPeriod] = myScheduleArray.FullTimeSchedules[employee][workPeriod]; + this.fullTimeSchedules[employee][workPeriod] = myScheduleArray.fullTimeSchedules[employee][workPeriod]; + this.initialFullTimeSchedules[employee][workPeriod] = myScheduleArray.initialFullTimeSchedules[employee][workPeriod]; } } @@ -61,9 +79,9 @@ public class SchedulesArray { this.workingPeriodsPerPartTimeEmployees = new int[this.maxPartTimeEmployee]; this.workingPeriodsPerFullTimeEmployees = new int[this.maxFullTimeEmployee]; - updateEmployeesPerWorkPeriod(); - updateWorkingPeriodsPerPartTimeEmployees(); - updateWorkingPeriodsPerFullTimeEmployees(); + updateAllEmployeesPerWorkPeriod(); + updateAllWorkingPeriodsPerPartTimeEmployees(); + updateAllWorkingPeriodsPerFullTimeEmployees(); } @@ -76,17 +94,35 @@ public class SchedulesArray { this.workPeriodsPerDay = myModelInitialSchedules.myScheduleParameters.workPeriodsPerDay; this.daysPerSchedule = myModelInitialSchedules.myScheduleParameters.daysPerSchedule; - this.PartTimeSchedules = new int[this.maxPartTimeEmployee][this.workPeriodsPerSchedule]; - this.FullTimeSchedules = new int[this.maxFullTimeEmployee][this.workPeriodsPerSchedule]; + this.isPartTimeEmployeeActive = new boolean[this.maxPartTimeEmployee]; + for (int employee = 0; employee < this.maxPartTimeEmployee; employee++) { + if (mySolution.getIntVal(myModelInitialSchedules.workingPeriodsPerPartTimeEmployees[employee]) > 0){ + this.isPartTimeEmployeeActive[employee] = true; + } + } + + this.isFullTimeEmployeeActive = new boolean[this.maxFullTimeEmployee]; + for (int employee = 0; employee < this.maxFullTimeEmployee; employee++) { + if (mySolution.getIntVal(myModelInitialSchedules.workingPeriodsPerFullTimeEmployees[employee]) > 0){ + this.isFullTimeEmployeeActive[employee] = true; + } + } + + this.partTimeSchedules = new boolean[this.maxPartTimeEmployee][this.workPeriodsPerSchedule]; + this.fullTimeSchedules = new boolean[this.maxFullTimeEmployee][this.workPeriodsPerSchedule]; + this.initialPartTimeSchedules = new boolean[this.maxPartTimeEmployee][this.workPeriodsPerSchedule]; + this.initialFullTimeSchedules = new boolean[this.maxFullTimeEmployee][this.workPeriodsPerSchedule]; for (int workPeriod = 0; workPeriod < this.workPeriodsPerSchedule; workPeriod++) { for (int employee = 0; employee < this.maxPartTimeEmployee; employee++) { - this.PartTimeSchedules[employee][workPeriod] = mySolution.getIntVal(myModelInitialSchedules.workPeriodsSchedulesOfPartTimeEmployees[employee][workPeriod]); + this.partTimeSchedules[employee][workPeriod] = mySolution.getIntVal(myModelInitialSchedules.workPeriodsSchedulesOfPartTimeEmployees[employee][workPeriod]) == 1; + this.initialPartTimeSchedules[employee][workPeriod] = mySolution.getIntVal(myModelInitialSchedules.workPeriodsSchedulesOfPartTimeEmployees[employee][workPeriod]) == 1; } } for (int workPeriod = 0; workPeriod < this.workPeriodsPerSchedule; workPeriod++) { for (int employee = 0; employee < this.maxFullTimeEmployee; employee++) { - this.FullTimeSchedules[employee][workPeriod] = mySolution.getIntVal(myModelInitialSchedules.workPeriodsSchedulesOfFullTimeEmployees[employee][workPeriod]); + this.fullTimeSchedules[employee][workPeriod] = mySolution.getIntVal(myModelInitialSchedules.workPeriodsSchedulesOfFullTimeEmployees[employee][workPeriod]) == 1; + this.initialFullTimeSchedules[employee][workPeriod] = mySolution.getIntVal(myModelInitialSchedules.workPeriodsSchedulesOfFullTimeEmployees[employee][workPeriod]) == 1; } } @@ -94,42 +130,52 @@ public class SchedulesArray { this.workingPeriodsPerPartTimeEmployees = new int[this.maxPartTimeEmployee]; this.workingPeriodsPerFullTimeEmployees = new int[this.maxFullTimeEmployee]; - updateEmployeesPerWorkPeriod(); - updateWorkingPeriodsPerPartTimeEmployees(); - updateWorkingPeriodsPerFullTimeEmployees(); + updateAllEmployeesPerWorkPeriod(); + updateAllWorkingPeriodsPerPartTimeEmployees(); + updateAllWorkingPeriodsPerFullTimeEmployees(); } - protected void updateEmployeesPerWorkPeriod() { + protected void updateAllEmployeesPerWorkPeriod() { for (int workPeriod = 0; workPeriod < this.workPeriodsPerSchedule; workPeriod++) { - this.employeesPerWorkPeriod[workPeriod] = 0; - for (int employee = 0; employee < this.maxPartTimeEmployee; employee++) { - this.employeesPerWorkPeriod[workPeriod] += this.PartTimeSchedules[employee][workPeriod]; - } - for (int employee = 0; employee < this.maxFullTimeEmployee; employee++) { - this.employeesPerWorkPeriod[workPeriod] += this.FullTimeSchedules[employee][workPeriod]; - } + updateEmployeesPerWorkPeriod(workPeriod); } } - protected void updateWorkingPeriodsPerPartTimeEmployees() { - + protected void updateEmployeesPerWorkPeriod(int workPeriod) { + this.employeesPerWorkPeriod[workPeriod] = 0; for (int employee = 0; employee < this.maxPartTimeEmployee; employee++) { - this.workingPeriodsPerPartTimeEmployees[employee] = 0; - for (int workPeriod = 0; workPeriod < this.workPeriodsPerSchedule; workPeriod++) { - this.workingPeriodsPerPartTimeEmployees[employee] += this.PartTimeSchedules[employee][workPeriod]; - } + if (this.partTimeSchedules[employee][workPeriod]) {this.employeesPerWorkPeriod[workPeriod] += 1;} + } + for (int employee = 0; employee < this.maxFullTimeEmployee; employee++) { + if (this.fullTimeSchedules[employee][workPeriod]) {this.employeesPerWorkPeriod[workPeriod] += 1;} } } - protected void updateWorkingPeriodsPerFullTimeEmployees() { - + protected void updateAllWorkingPeriodsPerPartTimeEmployees() { + for (int employee = 0; employee < this.maxPartTimeEmployee; employee++) { + updateWorkingPeriodsPerPartTimeEmployees(employee); + } + } + + protected void updateWorkingPeriodsPerPartTimeEmployees(int employee) { + this.workingPeriodsPerPartTimeEmployees[employee] = 0; + for (int workPeriod = 0; workPeriod < this.workPeriodsPerSchedule; workPeriod++) { + if (this.partTimeSchedules[employee][workPeriod]) {this.workingPeriodsPerPartTimeEmployees[employee] += 1;} + } + } + + protected void updateAllWorkingPeriodsPerFullTimeEmployees() { for (int employee = 0; employee < this.maxFullTimeEmployee; employee++) { - this.workingPeriodsPerFullTimeEmployees[employee] = 0; - for (int workPeriod = 0; workPeriod < this.workPeriodsPerSchedule; workPeriod++) { - this.workingPeriodsPerFullTimeEmployees[employee] += this.FullTimeSchedules[employee][workPeriod]; - } + updateWorkingPeriodsPerFullTimeEmployees(employee); + } + } + + protected void updateWorkingPeriodsPerFullTimeEmployees(int employee) { + this.workingPeriodsPerFullTimeEmployees[employee] = 0; + for (int workPeriod = 0; workPeriod < this.workPeriodsPerSchedule; workPeriod++) { + if (this.fullTimeSchedules[employee][workPeriod]) {this.workingPeriodsPerFullTimeEmployees[employee] += 1;} } } @@ -153,20 +199,28 @@ public class SchedulesArray { return workPeriodsPerDay; } - public int[][] getFullTimeSchedules() { - return FullTimeSchedules; + public boolean[][] getFullTimeSchedules() { + return fullTimeSchedules; } - public int[][] getPartTimeSchedules() { - return PartTimeSchedules; + public boolean[][] getPartTimeSchedules() { + return partTimeSchedules; } public boolean isPartTimeEmployeeWorking(int employee, int workPeriod) { - return PartTimeSchedules[employee][workPeriod] == 1; + return partTimeSchedules[employee][workPeriod] == true; } public boolean isFullTimeEmployeeWorking(int employee, int workPeriod) { - return FullTimeSchedules[employee][workPeriod] == 1; + return fullTimeSchedules[employee][workPeriod] == true; + } + + public boolean isPartTimeEmployeeActive(int employee) { + return isPartTimeEmployeeActive[employee] == true; + } + + public boolean isFullTimeEmployeeActive(int employee) { + return isFullTimeEmployeeActive[employee] == true; } public int getRequiredWorkForce(int workPeriod) { diff --git a/Travail_de_session/SchedulesRecovery/RecoveredSchedulesArray.java b/Travail_de_session/SchedulesRecovery/RecoveredSchedulesArray.java index 916137d..dc42230 100644 --- a/Travail_de_session/SchedulesRecovery/RecoveredSchedulesArray.java +++ b/Travail_de_session/SchedulesRecovery/RecoveredSchedulesArray.java @@ -6,7 +6,7 @@ package SchedulesRecovery; import AbsenceSchedules.AbsenceSchedulesArray; -import ScheduleUtil.SchedulesArray; + /** * @@ -14,11 +14,159 @@ import ScheduleUtil.SchedulesArray; */ public class RecoveredSchedulesArray extends AbsenceSchedulesArray{ - public RecoveredSchedulesArray(SchedulesArray myScheduleArray){ - super(myScheduleArray); + protected boolean[][] partTimeEmployeeAbsenceRecoveringAvailabilitySchedules; + protected boolean[][] fullTimeEmployeeAbsenceRecoveringAvailabilitySchedules; + + public RecoveredSchedulesArray(AbsenceSchedulesArray myAbsenceScheduleArray){ + super(myAbsenceScheduleArray); } public void recoverAbsenceScheduleOptimally() { + for (int workPeriod = 0; workPeriod < this.workPeriodsPerSchedule; workPeriod++) { + boolean noEmployeeAvailable = false; + while (!noEmployeeAvailable && getEmployeesPerWorkPeriod(workPeriod) < getRequiredWorkForce(workPeriod)) { + boolean absenceRecoveryDone = false; + int employee = 0; + while (!absenceRecoveryDone && employee < this.maxFullTimeEmployee) { + if (isFullTimeEmployeeAvailableForAbsenceRecovering(employee, workPeriod) && !isFullTimeEmployeeWorking(employee, workPeriod)){ + fullTimeSchedules[employee][workPeriod] = true; + updateEmployeesPerWorkPeriod(workPeriod); + updateWorkingPeriodsPerFullTimeEmployees(employee); + absenceRecoveryDone = true; + } + employee++; + } + employee = 0; + while (!absenceRecoveryDone && employee < this.maxPartTimeEmployee) { + if (isPartTimeEmployeeAvailableForAbsenceRecovering(employee, workPeriod) && !isPartTimeEmployeeWorking(employee, workPeriod)){ + partTimeSchedules[employee][workPeriod] = true; + updateEmployeesPerWorkPeriod(workPeriod); + updateWorkingPeriodsPerPartTimeEmployees(employee); + absenceRecoveryDone = true; + } + employee++; + } + if (!absenceRecoveryDone) { + noEmployeeAvailable = true; + } + } + + } } + + private boolean isPartTimeEmployeeAvailableForAbsenceRecovering(int employee, int workPeriod){ + return isPartTimeEmployeeActive(employee) && !isPartTimeEmployeeAbsent(employee, workPeriod) && isValidPartTimeEmployeeWorkPeriod(employee,workPeriod); + } + + private boolean isValidPartTimeEmployeeWorkPeriod(int employee, int workPeriod){ + return isValidWorkingPeriodsOfPartTimeEmployee(employee) && + isValidConsecutiveWorkingPeriodsOfPartTimeEmployee(employee, workPeriod) && + isValidConsecutiveNonWorkingPeriodsBetweenShiftWorksOfPartTimeEmployee(employee, workPeriod); + } + + private boolean isValidWorkingPeriodsOfPartTimeEmployee(int employee){ + return this.workingPeriodsPerPartTimeEmployees[employee] < myModelInitialSchedules.myScheduleParameters.maxWorkingPeriodsOfPartTimeEmployeesPerSchedule; + } + + private boolean isValidConsecutiveWorkingPeriodsOfPartTimeEmployee(int employee, int workPeriod){ + int consecutiveWorkingPeriods = 1; + int compteurWorkPeriod = workPeriod - 1; + while ( compteurWorkPeriod >= 0 && isPartTimeEmployeeWorking(employee, compteurWorkPeriod) ){ + consecutiveWorkingPeriods += 1; + compteurWorkPeriod--; + } + compteurWorkPeriod = workPeriod + 1; + while ( compteurWorkPeriod < this.workPeriodsPerSchedule && isPartTimeEmployeeWorking(employee, compteurWorkPeriod) ){ + consecutiveWorkingPeriods += 1; + compteurWorkPeriod++; + } + return consecutiveWorkingPeriods <= myModelInitialSchedules.myScheduleParameters.maxConsecutiveWorkingPeriodsOfPartTimeEmployeesPerShiftWork; + } + + private boolean isValidConsecutiveNonWorkingPeriodsBetweenShiftWorksOfPartTimeEmployee(int employee, int workPeriod){ + int consecutivePreviousNonWorkingPeriods = 0; + int compteurWorkPeriod = workPeriod - 1; + while ( compteurWorkPeriod >= 0 && !isPartTimeEmployeeWorking(employee, compteurWorkPeriod) ){ + consecutivePreviousNonWorkingPeriods += 1; + compteurWorkPeriod--; + } + boolean validConsecutivePreviousNonWorkingPeriods = false; + if (compteurWorkPeriod == -1) { + validConsecutivePreviousNonWorkingPeriods = true; + } else if (consecutivePreviousNonWorkingPeriods >= myModelInitialSchedules.myScheduleParameters.minConsecutiveNonWorkingPeriodsBetweenShiftWorksOfPartTimeEmployees) { + validConsecutivePreviousNonWorkingPeriods = true; + } + int consecutiveNextNonWorkingPeriods = 0; + compteurWorkPeriod = workPeriod + 1; + while ( compteurWorkPeriod < this.workPeriodsPerSchedule && !isPartTimeEmployeeWorking(employee, compteurWorkPeriod) ){ + consecutiveNextNonWorkingPeriods += 1; + compteurWorkPeriod++; + } + boolean validConsecutiveNextNonWorkingPeriods = false; + if (compteurWorkPeriod == workPeriodsPerSchedule) { + validConsecutiveNextNonWorkingPeriods = true; + } else if (consecutiveNextNonWorkingPeriods >= myModelInitialSchedules.myScheduleParameters.minConsecutiveNonWorkingPeriodsBetweenShiftWorksOfPartTimeEmployees) { + validConsecutiveNextNonWorkingPeriods = true; + } + return validConsecutivePreviousNonWorkingPeriods && validConsecutiveNextNonWorkingPeriods; + } + + private boolean isFullTimeEmployeeAvailableForAbsenceRecovering(int employee, int workPeriod){ + return isFullTimeEmployeeActive(employee) && !isFullTimeEmployeeAbsent(employee, workPeriod) && isValidFullTimeEmployeeWorkPeriod(employee,workPeriod); + } + + private boolean isValidFullTimeEmployeeWorkPeriod(int employee, int workPeriod){ + return isValidWorkingPeriodsOfFullTimeEmployee(employee) && + isValidConsecutiveWorkingPeriodsOfFullTimeEmployee(employee, workPeriod) && + isValidConsecutiveNonWorkingPeriodsBetweenShiftWorksOfFullTimeEmployee(employee, workPeriod); + } + + private boolean isValidWorkingPeriodsOfFullTimeEmployee(int employee){ + return this.workingPeriodsPerFullTimeEmployees[employee] < myModelInitialSchedules.myScheduleParameters.maxWorkingPeriodsOfFullTimeEmployeesPerSchedule; + } + + + private boolean isValidConsecutiveWorkingPeriodsOfFullTimeEmployee(int employee, int workPeriod){ + int consecutiveWorkingPeriods = 1; + int compteurWorkPeriod = workPeriod - 1; + while ( compteurWorkPeriod >= 0 && isFullTimeEmployeeWorking(employee, compteurWorkPeriod) ){ + consecutiveWorkingPeriods += 1; + compteurWorkPeriod--; + } + compteurWorkPeriod = workPeriod + 1; + while ( compteurWorkPeriod < this.workPeriodsPerSchedule && isFullTimeEmployeeWorking(employee, compteurWorkPeriod) ){ + consecutiveWorkingPeriods += 1; + compteurWorkPeriod++; + } + return consecutiveWorkingPeriods <= myModelInitialSchedules.myScheduleParameters.maxConsecutiveWorkingPeriodsOfFullTimeEmployeesPerShiftWork; + } + + private boolean isValidConsecutiveNonWorkingPeriodsBetweenShiftWorksOfFullTimeEmployee(int employee, int workPeriod){ + int consecutivePreviousNonWorkingPeriods = 0; + int compteurWorkPeriod = workPeriod - 1; + while ( compteurWorkPeriod >= 0 && !isFullTimeEmployeeWorking(employee, compteurWorkPeriod)){ + consecutivePreviousNonWorkingPeriods += 1; + compteurWorkPeriod--; + } + boolean validConsecutivePreviousNonWorkingPeriods = false; + if (compteurWorkPeriod == -1) { + validConsecutivePreviousNonWorkingPeriods = true; + } else if (consecutivePreviousNonWorkingPeriods >= myModelInitialSchedules.myScheduleParameters.minConsecutiveNonWorkingPeriodsBetweenShiftWorksOfFullTimeEmployees) { + validConsecutivePreviousNonWorkingPeriods = true; + } + int consecutiveNextNonWorkingPeriods = 0; + compteurWorkPeriod = workPeriod + 1; + while ( compteurWorkPeriod < this.workPeriodsPerSchedule && !isFullTimeEmployeeWorking(employee, compteurWorkPeriod) ){ + consecutiveNextNonWorkingPeriods += 1; + compteurWorkPeriod++; + } + boolean validConsecutiveNextNonWorkingPeriods = false; + if (compteurWorkPeriod == workPeriodsPerSchedule) { + validConsecutiveNextNonWorkingPeriods = true; + } else if (consecutiveNextNonWorkingPeriods >= myModelInitialSchedules.myScheduleParameters.minConsecutiveNonWorkingPeriodsBetweenShiftWorksOfFullTimeEmployees) { + validConsecutiveNextNonWorkingPeriods = true; + } + return validConsecutivePreviousNonWorkingPeriods && validConsecutiveNextNonWorkingPeriods; + } } From 04bfd630f1224d884ffa8e284f7697a354844f19 Mon Sep 17 00:00:00 2001 From: "Francois Berube\\frabe" Date: Mon, 2 Apr 2018 23:58:33 -0400 Subject: [PATCH 2/3] =?UTF-8?q?Correction=20de=20bugs=20li=C3=A9s=20aux=20?= =?UTF-8?q?contraintes=20de=20recouvrement=20d'horaire.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AbsenceSchedulesArray.java | 21 +++---------------- .../RecoveredSchedulesArray.java | 11 +++++++--- 2 files changed, 11 insertions(+), 21 deletions(-) diff --git a/Travail_de_session/AbsenceSchedules/AbsenceSchedulesArray.java b/Travail_de_session/AbsenceSchedules/AbsenceSchedulesArray.java index 75bb4ce..55aa0ab 100644 --- a/Travail_de_session/AbsenceSchedules/AbsenceSchedulesArray.java +++ b/Travail_de_session/AbsenceSchedules/AbsenceSchedulesArray.java @@ -6,39 +6,24 @@ import jdistlib.rng.RandomEngine; import org.chocosolver.solver.Solution; public class AbsenceSchedulesArray extends SchedulesArray{ - - protected boolean[][] partTimeEmployeeUnavailabilitySchedules; - protected boolean[][] fullTimeEmployeeUnavailabilitySchedules; - + public AbsenceSchedulesArray(SchedulesArray myScheduleArray) { super(myScheduleArray); } public AbsenceSchedulesArray(AbsenceSchedulesArray myAbsenceScheduleArray) { super(myAbsenceScheduleArray); - this.partTimeEmployeeUnavailabilitySchedules = new boolean[this.maxPartTimeEmployee][this.workPeriodsPerSchedule]; - for (int workPeriod = 0; workPeriod < this.workPeriodsPerSchedule; workPeriod++) { - for (int employee = 0; employee < this.maxPartTimeEmployee; employee++) { - this.partTimeEmployeeUnavailabilitySchedules[employee][workPeriod] = myAbsenceScheduleArray.partTimeEmployeeUnavailabilitySchedules[employee][workPeriod]; - } - } - this.fullTimeEmployeeUnavailabilitySchedules = new boolean[this.maxFullTimeEmployee][this.workPeriodsPerSchedule]; - for (int workPeriod = 0; workPeriod < this.workPeriodsPerSchedule; workPeriod++) { - for (int employee = 0; employee < this.maxFullTimeEmployee; employee++) { - this.fullTimeEmployeeUnavailabilitySchedules[employee][workPeriod] = myAbsenceScheduleArray.fullTimeEmployeeUnavailabilitySchedules[employee][workPeriod]; - } - } } public void generateAbsences(RandomEngine r) { - this.partTimeEmployeeUnavailabilitySchedules = new boolean[this.maxPartTimeEmployee][this.workPeriodsPerSchedule]; + for (int i = 0; i < this.maxPartTimeEmployee; i++) { boolean[] a = new AbsencesVector(this.workPeriodsPerSchedule, r).getAbsencesVector(); for (int j = 0; j < this.workPeriodsPerSchedule; j++) { this.partTimeSchedules[i][j] = this.initialPartTimeSchedules[i][j] && a[j]; } } - this.fullTimeEmployeeUnavailabilitySchedules = new boolean[this.maxFullTimeEmployee][this.workPeriodsPerSchedule]; + for (int i = 0; i < this.maxFullTimeEmployee; i++) { boolean[] a = new AbsencesVector(this.workPeriodsPerSchedule, r).getAbsencesVector(); for (int j = 0; j < this.workPeriodsPerSchedule; j++) { diff --git a/Travail_de_session/SchedulesRecovery/RecoveredSchedulesArray.java b/Travail_de_session/SchedulesRecovery/RecoveredSchedulesArray.java index dc42230..f506635 100644 --- a/Travail_de_session/SchedulesRecovery/RecoveredSchedulesArray.java +++ b/Travail_de_session/SchedulesRecovery/RecoveredSchedulesArray.java @@ -14,9 +14,6 @@ import AbsenceSchedules.AbsenceSchedulesArray; */ public class RecoveredSchedulesArray extends AbsenceSchedulesArray{ - protected boolean[][] partTimeEmployeeAbsenceRecoveringAvailabilitySchedules; - protected boolean[][] fullTimeEmployeeAbsenceRecoveringAvailabilitySchedules; - public RecoveredSchedulesArray(AbsenceSchedulesArray myAbsenceScheduleArray){ super(myAbsenceScheduleArray); } @@ -94,6 +91,8 @@ public class RecoveredSchedulesArray extends AbsenceSchedulesArray{ boolean validConsecutivePreviousNonWorkingPeriods = false; if (compteurWorkPeriod == -1) { validConsecutivePreviousNonWorkingPeriods = true; + } else if (consecutivePreviousNonWorkingPeriods == 0) { + validConsecutivePreviousNonWorkingPeriods = true; } else if (consecutivePreviousNonWorkingPeriods >= myModelInitialSchedules.myScheduleParameters.minConsecutiveNonWorkingPeriodsBetweenShiftWorksOfPartTimeEmployees) { validConsecutivePreviousNonWorkingPeriods = true; } @@ -106,6 +105,8 @@ public class RecoveredSchedulesArray extends AbsenceSchedulesArray{ boolean validConsecutiveNextNonWorkingPeriods = false; if (compteurWorkPeriod == workPeriodsPerSchedule) { validConsecutiveNextNonWorkingPeriods = true; + } else if (consecutiveNextNonWorkingPeriods == 0) { + validConsecutiveNextNonWorkingPeriods = true; } else if (consecutiveNextNonWorkingPeriods >= myModelInitialSchedules.myScheduleParameters.minConsecutiveNonWorkingPeriodsBetweenShiftWorksOfPartTimeEmployees) { validConsecutiveNextNonWorkingPeriods = true; } @@ -152,6 +153,8 @@ public class RecoveredSchedulesArray extends AbsenceSchedulesArray{ boolean validConsecutivePreviousNonWorkingPeriods = false; if (compteurWorkPeriod == -1) { validConsecutivePreviousNonWorkingPeriods = true; + } else if (consecutivePreviousNonWorkingPeriods == 0) { + validConsecutivePreviousNonWorkingPeriods = true; } else if (consecutivePreviousNonWorkingPeriods >= myModelInitialSchedules.myScheduleParameters.minConsecutiveNonWorkingPeriodsBetweenShiftWorksOfFullTimeEmployees) { validConsecutivePreviousNonWorkingPeriods = true; } @@ -164,6 +167,8 @@ public class RecoveredSchedulesArray extends AbsenceSchedulesArray{ boolean validConsecutiveNextNonWorkingPeriods = false; if (compteurWorkPeriod == workPeriodsPerSchedule) { validConsecutiveNextNonWorkingPeriods = true; + } else if (consecutiveNextNonWorkingPeriods == 0) { + validConsecutiveNextNonWorkingPeriods = true; } else if (consecutiveNextNonWorkingPeriods >= myModelInitialSchedules.myScheduleParameters.minConsecutiveNonWorkingPeriodsBetweenShiftWorksOfFullTimeEmployees) { validConsecutiveNextNonWorkingPeriods = true; } From d1a9072e46ddf84c2373c0f498afd548f256e823 Mon Sep 17 00:00:00 2001 From: "Francois Berube\\frabe" Date: Wed, 4 Apr 2018 00:24:31 -0400 Subject: [PATCH 3/3] =?UTF-8?q?D=C3=A9but=20du=20travail=20sur=20l'optimis?= =?UTF-8?q?eur=20de=20recouvrement.d'horaire=20pour=20trouver=20celle=20qu?= =?UTF-8?q?i=20conduit=20au=20co=C3=BBt=20minimal.=20Ajout=20d'une=20class?= =?UTF-8?q?e=20statique=20pour=20calculer=20les=20co=C3=BBts=20d'employ?= =?UTF-8?q?=C3=A9s=20pour=20un=20horaire=20donn=C3=A9.=20Ajout=20de=20la?= =?UTF-8?q?=20librairie=20java=20tuples=20pour=20avoir=20des=20quartets=20?= =?UTF-8?q?pour=20stocker=20des=20=C3=A9tats=20d'horaire=20et=20ainsi=20fa?= =?UTF-8?q?ire=20une=20fouille=20dans=20l'arbre=20de=20recouvrement=20d'ho?= =?UTF-8?q?raire.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AbsenceSchedules/AbsencesVector.java | 2 +- .../ModelInitialSchedules.java | 14 +- .../ParametersInitialSchedules.java | 117 ++++++---- Travail_de_session/MainClass.java | 4 +- .../ScheduleUtil/EmployeeCostCalculator.java | 89 ++++++++ .../ScheduleUtil/SchedulesArray.java | 41 ++-- .../RecoveredSchedulesArray.java | 208 +++++++++++++----- .../SchedulesRecovery/RecoveringAction.java | 33 +++ .../RecoveringActionFullTimeEmployee.java | 18 ++ .../RecoveringActionPartTimeEmployee.java | 19 ++ .../ScheduleStateComparator.java | 19 ++ Travail_de_session/javatuples-1.2.jar | Bin 0 -> 65508 bytes 12 files changed, 437 insertions(+), 127 deletions(-) create mode 100644 Travail_de_session/ScheduleUtil/EmployeeCostCalculator.java create mode 100644 Travail_de_session/SchedulesRecovery/RecoveringAction.java create mode 100644 Travail_de_session/SchedulesRecovery/RecoveringActionFullTimeEmployee.java create mode 100644 Travail_de_session/SchedulesRecovery/RecoveringActionPartTimeEmployee.java create mode 100644 Travail_de_session/SchedulesRecovery/ScheduleStateComparator.java create mode 100644 Travail_de_session/javatuples-1.2.jar diff --git a/Travail_de_session/AbsenceSchedules/AbsencesVector.java b/Travail_de_session/AbsenceSchedules/AbsencesVector.java index 46351e9..cf09152 100644 --- a/Travail_de_session/AbsenceSchedules/AbsencesVector.java +++ b/Travail_de_session/AbsenceSchedules/AbsencesVector.java @@ -5,7 +5,7 @@ import jdistlib.rng.RandomEngine; public class AbsencesVector { private boolean[] AbsencesVector; - private double probPresence = 0.90; + private double probPresence = 0.80; private double probReturn = 0.50; public AbsencesVector(int length, RandomEngine r) { diff --git a/Travail_de_session/InitialSchedules/ModelInitialSchedules.java b/Travail_de_session/InitialSchedules/ModelInitialSchedules.java index f899b7f..33b1a65 100644 --- a/Travail_de_session/InitialSchedules/ModelInitialSchedules.java +++ b/Travail_de_session/InitialSchedules/ModelInitialSchedules.java @@ -58,9 +58,9 @@ public class ModelInitialSchedules { private void createEmployeesVariables() { this.maxPartTimeEmployee = (int) Math.ceil((double) myScheduleParameters.totalWorkedPeriodsInSchedule - / myScheduleParameters.minWorkingPeriodsOfPartTimeEmployeesPerSchedule); + / myScheduleParameters.getMinWorkingPeriodsOfPartTimeEmployeesPerSchedule()); this.maxFullTimeEmployee = (int) Math.ceil((double) myScheduleParameters.totalWorkedPeriodsInSchedule - / myScheduleParameters.workingPeriodsOfFullTimeEmployeesPerSchedule); + / myScheduleParameters.getWorkingPeriodsOfFullTimeEmployeesPerSchedule()); } @@ -155,9 +155,9 @@ public class ModelInitialSchedules { chocoModelInitialSchedules.ifThen(isWorkingEmployee, chocoModelInitialSchedules.arithm( this.workingPeriodsPerPartTimeEmployees[employee], ">=", - this.myScheduleParameters.minWorkingPeriodsOfPartTimeEmployeesPerSchedule)); + this.myScheduleParameters.getMinWorkingPeriodsOfPartTimeEmployeesPerSchedule())); chocoModelInitialSchedules.arithm(this.workingPeriodsPerPartTimeEmployees[employee], "<=", - this.myScheduleParameters.maxWorkingPeriodsOfPartTimeEmployeesPerSchedule).post(); + this.myScheduleParameters.getMaxWorkingPeriodsOfPartTimeEmployeesPerSchedule()).post(); } // Constraintes pour compter le nombre d'employes par periode de travail et s'assurer qu'il @@ -237,7 +237,7 @@ public class ModelInitialSchedules { IntVar PartTimeEmployeesSalary = chocoModelInitialSchedules.intVar(0, this.maxPartTimeEmployee * this.myScheduleParameters.workPeriodsPerSchedule * - this.myScheduleParameters.hourlyRateOfPartTimeEmployees, true); + this.myScheduleParameters.regularHourlyRateOfPartTimeEmployees, true); IntVar FullTimeEmployeesSalary = chocoModelInitialSchedules.intVar(0, this.maxFullTimeEmployee * @@ -246,13 +246,13 @@ public class ModelInitialSchedules { this.TotalEmployeesSalary = chocoModelInitialSchedules.intVar(0, this.maxPartTimeEmployee * this.myScheduleParameters.workPeriodsPerSchedule * - this.myScheduleParameters.hourlyRateOfPartTimeEmployees + + this.myScheduleParameters.regularHourlyRateOfPartTimeEmployees + this.maxFullTimeEmployee * this.myScheduleParameters.workPeriodsPerSchedule * this.myScheduleParameters.regularHourlyRateOfFullTimeEmployees, true); IntVar HourlyRateOfPartTimeEmployees = - chocoModelInitialSchedules.intVar(this.myScheduleParameters.hourlyRateOfPartTimeEmployees); + chocoModelInitialSchedules.intVar(this.myScheduleParameters.regularHourlyRateOfPartTimeEmployees); IntVar regularHourlyRateOfFullTimeEmployees = chocoModelInitialSchedules.intVar(this.myScheduleParameters.regularHourlyRateOfFullTimeEmployees); diff --git a/Travail_de_session/InitialSchedules/ParametersInitialSchedules.java b/Travail_de_session/InitialSchedules/ParametersInitialSchedules.java index 2208548..73335c9 100644 --- a/Travail_de_session/InitialSchedules/ParametersInitialSchedules.java +++ b/Travail_de_session/InitialSchedules/ParametersInitialSchedules.java @@ -11,26 +11,24 @@ public class ParametersInitialSchedules { int totalWorkedPeriodsInSchedule; int hoursPerWorkPeriod; - int fixedCostOfPartTimeEmployeesPerSchedule; - int hourlyRateOfPartTimeEmployees; + public int fixedCostOfPartTimeEmployeesPerSchedule; + public int regularHourlyRateOfPartTimeEmployees; + public int overtimeHourlyRateOfPartTimeEmployees; int minWorkingHoursOfPartTimeEmployeesPerSchedule; int maxWorkingHoursOfPartTimeEmployeesPerSchedule; - int minWorkingPeriodsOfPartTimeEmployeesPerSchedule; - public int maxConsecutiveWorkingPeriodsOfPartTimeEmployeesPerShiftWork; - public int minConsecutiveNonWorkingPeriodsBetweenShiftWorksOfPartTimeEmployees; - public int maxWorkingPeriodsOfPartTimeEmployeesPerSchedule; + int maxConsecutiveWorkingHoursOfPartTimeEmployeesPerShiftWork; + int minConsecutiveNonWorkingHoursBetweenShiftWorksOfPartTimeEmployees; - int fixedCostOfFullTimeEmployeesPerSchedule; - int regularHourlyRateOfFullTimeEmployees; - int overtimeHourlyRateOfFullTimeEmployees; - int workingHoursOfFullTimeEmployeesPerSchedule; + public int fixedCostOfFullTimeEmployeesPerSchedule; + public int regularHourlyRateOfFullTimeEmployees; + public int overtimeHourlyRateOfFullTimeEmployees; int maxWorkingHoursOfFullTimeEmployeesPerSchedule; - int workingPeriodsOfFullTimeEmployeesPerSchedule; - public int maxConsecutiveWorkingPeriodsOfFullTimeEmployeesPerShiftWork; - public int minConsecutiveNonWorkingPeriodsBetweenShiftWorksOfFullTimeEmployees; - public int maxWorkingPeriodsOfFullTimeEmployeesPerSchedule; + int workingHoursOfFullTimeEmployeesPerSchedule; + int maxConsecutiveWorkingHoursOfFullTimeEmployeesPerShiftWork; + int minConsecutiveNonWorkingHoursBetweenShiftWorksOfFullTimeEmployees; int workingHoursPaidAtRegularHourlyRatePerSchedule; + int workingHoursPaidAtRegularHourlyRatePerShiftWork; public int[] requiredWorkforce; Tuples enumerationWorkPeriodsSchedulesOfFullTimeEmployees; @@ -54,44 +52,28 @@ public class ParametersInitialSchedules { this.daysPerSchedule = 14; this.hoursPerWorkPeriod = 24 / this.workPeriodsPerDay; this.workPeriodsPerSchedule = this.workPeriodsPerDay * this.daysPerSchedule; + this.workingHoursPaidAtRegularHourlyRatePerSchedule = 80; + this.workingHoursPaidAtRegularHourlyRatePerShiftWork = 8; } private void setPartTimeEmployeesParameters() { this.fixedCostOfPartTimeEmployeesPerSchedule = 50; - this.hourlyRateOfPartTimeEmployees = 12; // To simulate lower productivity + this.regularHourlyRateOfPartTimeEmployees = 12; // To simulate lower productivity + this.overtimeHourlyRateOfPartTimeEmployees = 17; // To simulate lower productivity this.minWorkingHoursOfPartTimeEmployeesPerSchedule = 32; this.maxWorkingHoursOfPartTimeEmployeesPerSchedule = 64; - int maxConsecutiveWorkingHoursOfPartTimeEmployeesPerShiftWork = 12; - this.maxConsecutiveWorkingPeriodsOfPartTimeEmployeesPerShiftWork - = (int) (maxConsecutiveWorkingHoursOfPartTimeEmployeesPerShiftWork / this.hoursPerWorkPeriod); - int minConsecutiveNonWorkingHoursBetweenShiftWorksOfPartTimeEmployees = 12; - this.minConsecutiveNonWorkingPeriodsBetweenShiftWorksOfPartTimeEmployees - = (int) (minConsecutiveNonWorkingHoursBetweenShiftWorksOfPartTimeEmployees / this.hoursPerWorkPeriod); - this.minWorkingPeriodsOfPartTimeEmployeesPerSchedule - = (int) (this.minWorkingHoursOfPartTimeEmployeesPerSchedule / this.hoursPerWorkPeriod); - this.maxWorkingPeriodsOfPartTimeEmployeesPerSchedule - = (int) (this.maxWorkingHoursOfPartTimeEmployeesPerSchedule / this.hoursPerWorkPeriod); + this.maxConsecutiveWorkingHoursOfPartTimeEmployeesPerShiftWork = 12; + this.minConsecutiveNonWorkingHoursBetweenShiftWorksOfPartTimeEmployees = 12; } private void setFullTimeEmployeesParameters() { this.fixedCostOfFullTimeEmployeesPerSchedule = 50; this.regularHourlyRateOfFullTimeEmployees = 10; this.overtimeHourlyRateOfFullTimeEmployees = 15; - this.workingHoursOfFullTimeEmployeesPerSchedule = 80; this.maxWorkingHoursOfFullTimeEmployeesPerSchedule = 120; - int maxConsecutiveWorkingHoursOfFullTimeEmployeesPerShiftWork = 12; - this.maxConsecutiveWorkingPeriodsOfFullTimeEmployeesPerShiftWork - = (int) (maxConsecutiveWorkingHoursOfFullTimeEmployeesPerShiftWork / this.hoursPerWorkPeriod); - int minConsecutiveNonWorkingHoursBetweenShiftWorksOfFullTimeEmployees = 12; - this.minConsecutiveNonWorkingPeriodsBetweenShiftWorksOfFullTimeEmployees - = (int) (minConsecutiveNonWorkingHoursBetweenShiftWorksOfFullTimeEmployees / this.hoursPerWorkPeriod); - this.workingPeriodsOfFullTimeEmployeesPerSchedule - = (int) (this.workingHoursOfFullTimeEmployeesPerSchedule / this.hoursPerWorkPeriod); - this.maxWorkingPeriodsOfFullTimeEmployeesPerSchedule - = (int) (this.maxWorkingHoursOfFullTimeEmployeesPerSchedule / this.hoursPerWorkPeriod); - - this.workingHoursPaidAtRegularHourlyRatePerSchedule = 80; + this.maxConsecutiveWorkingHoursOfFullTimeEmployeesPerShiftWork = 12; + this.minConsecutiveNonWorkingHoursBetweenShiftWorksOfFullTimeEmployees = 12; } private void setRequiredWorkforce() { @@ -163,7 +145,64 @@ public class ParametersInitialSchedules { this.ValidPartTimeEmployeeShiftTuples = new ValidPartTimeEmployeeShift().makeTuples(); } - + public int getWorkingPeriodsPaidAtRegularHourlyRatePerSchedule () { + return (int) (workingHoursPaidAtRegularHourlyRatePerSchedule / this.hoursPerWorkPeriod); + } + + public int getWorkingPeriodsPaidAtRegularHourlyRatePerShiftWork () { + return (int) (workingHoursPaidAtRegularHourlyRatePerShiftWork / this.hoursPerWorkPeriod); + } + + public int getWorkingPeriodCostOfPartTimeEmployeesPaidAtRegularHourlyRate () { + return (int) (regularHourlyRateOfPartTimeEmployees * this.hoursPerWorkPeriod); + } + + public int getWorkingPeriodCostOfFullTimeEmployeesPaidAtRegularHourlyRate () { + return (int) (regularHourlyRateOfFullTimeEmployees * this.hoursPerWorkPeriod); + } + + public int getWorkingPeriodCostOfPartTimeEmployeesPaidAtOvertimeHourlyRate () { + return (int) (overtimeHourlyRateOfPartTimeEmployees * this.hoursPerWorkPeriod); + } + + public int getWorkingPeriodCostOfFullTimeEmployeesPaidAtOvertimeHourlyRate () { + return (int) (overtimeHourlyRateOfFullTimeEmployees * this.hoursPerWorkPeriod); + } + + public int getMaxConsecutiveWorkingPeriodsOfFullTimeEmployeesPerShiftWork () { + return (int) (maxConsecutiveWorkingHoursOfFullTimeEmployeesPerShiftWork / this.hoursPerWorkPeriod); + } + + public int getMaxConsecutiveWorkingPeriodsOfPartTimeEmployeesPerShiftWork () { + return (int) (maxConsecutiveWorkingHoursOfPartTimeEmployeesPerShiftWork / this.hoursPerWorkPeriod); + } + + public int getMinConsecutiveNonWorkingPeriodsBetweenShiftWorksOfPartTimeEmployees () { + return (int) (minConsecutiveNonWorkingHoursBetweenShiftWorksOfPartTimeEmployees / this.hoursPerWorkPeriod); + } + + public int getMinConsecutiveNonWorkingPeriodsBetweenShiftWorksOfFullTimeEmployees () { + return (int) (minConsecutiveNonWorkingHoursBetweenShiftWorksOfFullTimeEmployees / this.hoursPerWorkPeriod); + } + + public int getMaxWorkingPeriodsOfPartTimeEmployeesPerSchedule () { + return (int) (this.maxWorkingHoursOfPartTimeEmployeesPerSchedule / this.hoursPerWorkPeriod); + } + + public int getMaxWorkingPeriodsOfFullTimeEmployeesPerSchedule () { + return (int) (this.maxWorkingHoursOfFullTimeEmployeesPerSchedule / this.hoursPerWorkPeriod); + } + + public int getMinWorkingPeriodsOfPartTimeEmployeesPerSchedule () { + return (int) (this.minWorkingHoursOfPartTimeEmployeesPerSchedule / this.hoursPerWorkPeriod); + + } + + public int getWorkingPeriodsOfFullTimeEmployeesPerSchedule () { + return (int) (this.workingHoursOfFullTimeEmployeesPerSchedule / this.hoursPerWorkPeriod); + } + + // A implementer plus tard si l'on veut travailler avec des fichiers texte public ParametersInitialSchedules(String fileName) { diff --git a/Travail_de_session/MainClass.java b/Travail_de_session/MainClass.java index 4970690..396b5e5 100644 --- a/Travail_de_session/MainClass.java +++ b/Travail_de_session/MainClass.java @@ -24,7 +24,7 @@ public class MainClass { List absenceSchedulesArrayList = GenerateAbsencesSchedules(initialSchedulesArrayList); - List recoveredSchedulesArrayList = GenerateRecoveredSchedules(absenceSchedulesArrayList); + List recoveredSchedulesArrayList = GenerateOptimalRecoveredSchedules(absenceSchedulesArrayList); // Algo de recouvrement d'absences. Faire un nouveau package de fonctions. // Trouver meilleure solution et l'afficher. } @@ -90,7 +90,7 @@ public class MainClass { return absenceSchedulesArrayList; } - private static List GenerateRecoveredSchedules(List absenceSchedulesArrayList) { + private static List GenerateOptimalRecoveredSchedules(List absenceSchedulesArrayList) { List recoveredSchedulesArrayList = new ArrayList<>(); for (SchedulesArray absenceSchedule : absenceSchedulesArrayList) { diff --git a/Travail_de_session/ScheduleUtil/EmployeeCostCalculator.java b/Travail_de_session/ScheduleUtil/EmployeeCostCalculator.java new file mode 100644 index 0000000..fdc0c92 --- /dev/null +++ b/Travail_de_session/ScheduleUtil/EmployeeCostCalculator.java @@ -0,0 +1,89 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package ScheduleUtil; + +import InitialSchedules.ParametersInitialSchedules; +import SchedulesRecovery.*; +/** + * + * @author frabe + */ +public class EmployeeCostCalculator { + + public static int getFullScheduleCost( boolean[][] partTimeEmployeesSchedule , boolean[][] fullTimeEmployeesSchedule, ParametersInitialSchedules myScheduleParameters) { + return 0; + } + + public static int getRecoveringActionCost (RecoveringAction action, ParametersInitialSchedules myScheduleParameters) { + int actionCost = 0; + if (action.getClass().getName() == "RecoveringActionPartTimeEmployee") { + actionCost = getPartTimeEmployeeCost(action.employeeScheduleAfterRecoveringAction, myScheduleParameters) - + getPartTimeEmployeeCost(action.employeeScheduleBeforeRecoveringAction, myScheduleParameters); + } else if (action.getClass().getName() == "RecoveringActionFullTimeEmployee") { + actionCost = getFullTimeEmployeeCost(action.employeeScheduleAfterRecoveringAction, myScheduleParameters) - + getFullTimeEmployeeCost(action.employeeScheduleBeforeRecoveringAction, myScheduleParameters); + } + return actionCost; + } + + public static int getPartTimeEmployeeCost( boolean[] employeeSchedule, ParametersInitialSchedules myScheduleParameters) { + + int consecutiveWorkingPeriods = 0; + int overtimeWorkingPeriods = 0; + int totalWorkingPeriods = 0; + for (int workPeriod = 0; workPeriod < employeeSchedule.length ; workPeriod++) { + if (employeeSchedule[workPeriod]) { + totalWorkingPeriods += 1; + consecutiveWorkingPeriods += 1; + if (consecutiveWorkingPeriods > myScheduleParameters.getWorkingPeriodsPaidAtRegularHourlyRatePerShiftWork()) { + overtimeWorkingPeriods += 1; + } + } else { + consecutiveWorkingPeriods = 0; + } + } + + int additionalOvertimeWorkingPeriods = totalWorkingPeriods - myScheduleParameters.getWorkingPeriodsPaidAtRegularHourlyRatePerSchedule(); + if ( additionalOvertimeWorkingPeriods > 0 ){ + overtimeWorkingPeriods += additionalOvertimeWorkingPeriods; + } + + int employeeCost = myScheduleParameters.fixedCostOfPartTimeEmployeesPerSchedule + + overtimeWorkingPeriods * myScheduleParameters.getWorkingPeriodCostOfPartTimeEmployeesPaidAtOvertimeHourlyRate() + + (totalWorkingPeriods - overtimeWorkingPeriods) * myScheduleParameters.getWorkingPeriodCostOfPartTimeEmployeesPaidAtRegularHourlyRate(); + + return employeeCost; + } + + public static int getFullTimeEmployeeCost( boolean[] employeeSchedule, ParametersInitialSchedules myScheduleParameters) { + + int consecutiveWorkingPeriods = 0; + int overtimeWorkingPeriods = 0; + int totalWorkingPeriods = 0; + for (int workPeriod = 0; workPeriod < employeeSchedule.length ; workPeriod++) { + if (employeeSchedule[workPeriod]) { + totalWorkingPeriods += 1; + consecutiveWorkingPeriods += 1; + if (consecutiveWorkingPeriods > myScheduleParameters.getWorkingPeriodsPaidAtRegularHourlyRatePerShiftWork()) { + overtimeWorkingPeriods += 1; + } + } else { + consecutiveWorkingPeriods = 0; + } + } + + int additionalOvertimeWorkingPeriods = totalWorkingPeriods - myScheduleParameters.getWorkingPeriodsPaidAtRegularHourlyRatePerSchedule(); + if ( additionalOvertimeWorkingPeriods > 0 ){ + overtimeWorkingPeriods += additionalOvertimeWorkingPeriods; + } + + int employeeCost = myScheduleParameters.fixedCostOfFullTimeEmployeesPerSchedule + + overtimeWorkingPeriods * myScheduleParameters.getWorkingPeriodCostOfFullTimeEmployeesPaidAtOvertimeHourlyRate() + + (totalWorkingPeriods - overtimeWorkingPeriods) * myScheduleParameters.getWorkingPeriodCostOfFullTimeEmployeesPaidAtRegularHourlyRate(); + + return employeeCost; + } +} diff --git a/Travail_de_session/ScheduleUtil/SchedulesArray.java b/Travail_de_session/ScheduleUtil/SchedulesArray.java index b21b14a..666f53a 100644 --- a/Travail_de_session/ScheduleUtil/SchedulesArray.java +++ b/Travail_de_session/ScheduleUtil/SchedulesArray.java @@ -57,23 +57,10 @@ public class SchedulesArray { this.isFullTimeEmployeeActive[employee] = myScheduleArray.isFullTimeEmployeeActive[employee]; } - this.partTimeSchedules = new boolean[this.maxPartTimeEmployee][this.workPeriodsPerSchedule]; - this.fullTimeSchedules = new boolean[this.maxFullTimeEmployee][this.workPeriodsPerSchedule]; - this.initialPartTimeSchedules = new boolean[this.maxPartTimeEmployee][this.workPeriodsPerSchedule]; - this.initialFullTimeSchedules = new boolean[this.maxFullTimeEmployee][this.workPeriodsPerSchedule]; - for (int workPeriod = 0; workPeriod < this.workPeriodsPerSchedule; workPeriod++) { - for (int employee = 0; employee < this.maxPartTimeEmployee; employee++) { - this.partTimeSchedules[employee][workPeriod] = myScheduleArray.partTimeSchedules[employee][workPeriod]; - this.initialPartTimeSchedules[employee][workPeriod] = myScheduleArray.initialPartTimeSchedules[employee][workPeriod]; - } - } - - for (int workPeriod = 0; workPeriod < this.workPeriodsPerSchedule; workPeriod++) { - for (int employee = 0; employee < this.maxFullTimeEmployee; employee++) { - this.fullTimeSchedules[employee][workPeriod] = myScheduleArray.fullTimeSchedules[employee][workPeriod]; - this.initialFullTimeSchedules[employee][workPeriod] = myScheduleArray.initialFullTimeSchedules[employee][workPeriod]; - } - } + this.partTimeSchedules = deepCopyPartTimeEmployeesSchedule(myScheduleArray.partTimeSchedules); + this.initialPartTimeSchedules = deepCopyPartTimeEmployeesSchedule(myScheduleArray.initialPartTimeSchedules); + this.fullTimeSchedules = deepCopyFullTimeEmployeesSchedule(myScheduleArray.fullTimeSchedules); + this.initialFullTimeSchedules = deepCopyFullTimeEmployeesSchedule(myScheduleArray.initialFullTimeSchedules); this.employeesPerWorkPeriod = new int[this.workPeriodsPerSchedule]; this.workingPeriodsPerPartTimeEmployees = new int[this.maxPartTimeEmployee]; @@ -85,6 +72,26 @@ public class SchedulesArray { } + protected boolean[][] deepCopyPartTimeEmployeesSchedule(boolean[][] partTimeSchedules) { + boolean[][] copyPartTimeSchedules = new boolean[this.maxPartTimeEmployee][this.workPeriodsPerSchedule]; + for (int workPeriod = 0; workPeriod < this.workPeriodsPerSchedule; workPeriod++) { + for (int employee = 0; employee < this.maxPartTimeEmployee; employee++) { + copyPartTimeSchedules[employee][workPeriod] = partTimeSchedules[employee][workPeriod]; + } + } + return copyPartTimeSchedules; + } + + protected boolean[][] deepCopyFullTimeEmployeesSchedule(boolean[][] fullTimeSchedules) { + boolean[][] copyFullTimeSchedules = new boolean[this.maxFullTimeEmployee][this.workPeriodsPerSchedule]; + for (int workPeriod = 0; workPeriod < this.workPeriodsPerSchedule; workPeriod++) { + for (int employee = 0; employee < this.maxFullTimeEmployee; employee++) { + copyFullTimeSchedules[employee][workPeriod] = fullTimeSchedules[employee][workPeriod]; + } + } + return copyFullTimeSchedules; + } + public SchedulesArray(ModelInitialSchedules m, Solution s) { this.myModelInitialSchedules = m; Solution mySolution = s; diff --git a/Travail_de_session/SchedulesRecovery/RecoveredSchedulesArray.java b/Travail_de_session/SchedulesRecovery/RecoveredSchedulesArray.java index f506635..2ea3241 100644 --- a/Travail_de_session/SchedulesRecovery/RecoveredSchedulesArray.java +++ b/Travail_de_session/SchedulesRecovery/RecoveredSchedulesArray.java @@ -6,7 +6,9 @@ package SchedulesRecovery; import AbsenceSchedules.AbsenceSchedulesArray; - +import java.util.*; +import org.javatuples.Triplet; +import ScheduleUtil.*; /** * @@ -18,73 +20,151 @@ public class RecoveredSchedulesArray extends AbsenceSchedulesArray{ super(myAbsenceScheduleArray); } +// public void oldRecoverAbsenceScheduleOptimally() { +// for ( int workPeriod = 0 ; workPeriod < this.workPeriodsPerSchedule ; workPeriod++ ) { +// boolean noEmployeeAvailable = false; +// while (!noEmployeeAvailable && getEmployeesPerWorkPeriod(workPeriod) < getRequiredWorkForce(workPeriod)) { +// boolean absenceRecoveryDone = false; +// int employee = 0; +// while (!absenceRecoveryDone && employee < this.maxFullTimeEmployee) { +// if ( isFullTimeEmployeeAvailableForAbsenceRecovering(employee, workPeriod) ){ +// fullTimeSchedules[employee][workPeriod] = true; +// updateEmployeesPerWorkPeriod(workPeriod); +// updateWorkingPeriodsPerFullTimeEmployees(employee); +// absenceRecoveryDone = true; +// } +// employee++; +// } +// employee = 0; +// while (!absenceRecoveryDone && employee < this.maxPartTimeEmployee) { +// if (isPartTimeEmployeeAvailableForAbsenceRecovering(employee, workPeriod) ){ +// partTimeSchedules[employee][workPeriod] = true; +// updateEmployeesPerWorkPeriod(workPeriod); +// updateWorkingPeriodsPerPartTimeEmployees(employee); +// absenceRecoveryDone = true; +// } +// employee++; +// } +// if (!absenceRecoveryDone) { +// noEmployeeAvailable = true; +// } +// } +// } +// +// } + public void recoverAbsenceScheduleOptimally() { - for (int workPeriod = 0; workPeriod < this.workPeriodsPerSchedule; workPeriod++) { - boolean noEmployeeAvailable = false; - while (!noEmployeeAvailable && getEmployeesPerWorkPeriod(workPeriod) < getRequiredWorkForce(workPeriod)) { - boolean absenceRecoveryDone = false; - int employee = 0; - while (!absenceRecoveryDone && employee < this.maxFullTimeEmployee) { - if (isFullTimeEmployeeAvailableForAbsenceRecovering(employee, workPeriod) && !isFullTimeEmployeeWorking(employee, workPeriod)){ - fullTimeSchedules[employee][workPeriod] = true; - updateEmployeesPerWorkPeriod(workPeriod); - updateWorkingPeriodsPerFullTimeEmployees(employee); - absenceRecoveryDone = true; - } - employee++; - } - employee = 0; - while (!absenceRecoveryDone && employee < this.maxPartTimeEmployee) { - if (isPartTimeEmployeeAvailableForAbsenceRecovering(employee, workPeriod) && !isPartTimeEmployeeWorking(employee, workPeriod)){ - partTimeSchedules[employee][workPeriod] = true; - updateEmployeesPerWorkPeriod(workPeriod); - updateWorkingPeriodsPerPartTimeEmployees(employee); - absenceRecoveryDone = true; - } - employee++; - } - if (!absenceRecoveryDone) { - noEmployeeAvailable = true; + int workPeriod = 0; + boolean[][] currentRecoveredScheduleOfPartTimeEmployees = deepCopyPartTimeEmployeesSchedule(partTimeSchedules); + boolean[][] currentRecoveredScheduleOfFullTimeEmployees = deepCopyFullTimeEmployeesSchedule(fullTimeSchedules); + int totalEmployeesCost = EmployeeCostCalculator.getFullScheduleCost(currentRecoveredScheduleOfPartTimeEmployees, currentRecoveredScheduleOfFullTimeEmployees, this.myModelInitialSchedules.myScheduleParameters); + int currentTotalEmployeesCost = totalEmployeesCost; + ScheduleStateComparator totalCostComparator = new ScheduleStateComparator(); + // mettre les Schedules dans les Tuples currentRecoveredScheduleOfPartTimeEmployees et currentRecoveredScheduleOfFullTimeEmployees au lieu de previousActions + PriorityQueue< Triplet > priorityQueueScheduleStatesOrderedByTotalCost = new PriorityQueue<>(totalCostComparator); + List< Triplet > listVisitedScheduleStates = new ArrayList<>(); + RecoveringAction currentRecoveringAction = null; + RecoveringAction previousRecoveringAction = null; + + while ( workPeriod < this.workPeriodsPerSchedule ) { + + Stack stackRecoveringActionsOfPartTimeEmployee = + getPossibleActionOfPartTimeEmployees(workPeriod, currentRecoveredScheduleOfPartTimeEmployees); + Stack stackRecoveringActionsOfFullTimeEmployees = + getPossibleActionOfFullTimeEmployees(workPeriod, currentRecoveredScheduleOfFullTimeEmployees); + while ( !stackRecoveringActionsOfPartTimeEmployee.isEmpty() && !stackRecoveringActionsOfFullTimeEmployees.isEmpty() ) { + if (stackRecoveringActionsOfPartTimeEmployee.isEmpty()) { + currentRecoveringAction = stackRecoveringActionsOfFullTimeEmployees.pop(); + } else { + currentRecoveringAction = stackRecoveringActionsOfPartTimeEmployee.pop(); } + + // On pourrait comparer currentRecoveredScheduleOfPartTimeEmployees pour voir si on ne l'a pas deja visite avec un coût plus faible. Dans ce cas, on ne pousse pas l'etat dans la liste. + int actionCost = EmployeeCostCalculator.getRecoveringActionCost( currentRecoveringAction, this.myModelInitialSchedules.myScheduleParameters ); + totalEmployeesCost = currentTotalEmployeesCost + actionCost; + Triplet scheduleState = Triplet.with(previousRecoveringAction, currentRecoveringAction, totalEmployeesCost); + priorityQueueScheduleStatesOrderedByTotalCost.add(scheduleState); + listVisitedScheduleStates.add(scheduleState); } + Triplet currentScheduleState = priorityQueueScheduleStatesOrderedByTotalCost.poll(); + currentTotalEmployeesCost = currentScheduleState.getValue2(); + previousRecoveringAction = currentScheduleState.getValue1(); + + //recalculer les currentRecoveredScheduleOfPartTimeEmployees et ajuster la workPeriod + //en trouvent la workPeriod la plus petite avec une demande incomplète. + //workPeriod++; } + } + + private Stack getPossibleActionOfPartTimeEmployees(int workPeriod, boolean[][] currentRecoveredSchedule) { + // inclure un attribut pour dire si la requiredWorkForce est satifaite avec l'action. + Stack stackRecoveringActionsOfPartTimeEmployee = new Stack<>(); + for (int employee = 0 ; employee < this.maxPartTimeEmployee ; employee++) { + boolean[] currentEmployeeSchedule = currentRecoveredSchedule[employee]; + if ( isPartTimeEmployeeActive(employee) && isPartTimeEmployeeAvailableForAbsenceRecovering(employee, workPeriod, currentEmployeeSchedule) ){ + RecoveringActionPartTimeEmployee recoveringAction = + new RecoveringActionPartTimeEmployee(employee, workPeriod, currentEmployeeSchedule); + stackRecoveringActionsOfPartTimeEmployee.push(recoveringAction); + } + } + return stackRecoveringActionsOfPartTimeEmployee; } - private boolean isPartTimeEmployeeAvailableForAbsenceRecovering(int employee, int workPeriod){ - return isPartTimeEmployeeActive(employee) && !isPartTimeEmployeeAbsent(employee, workPeriod) && isValidPartTimeEmployeeWorkPeriod(employee,workPeriod); + private Stack getPossibleActionOfFullTimeEmployees(int workPeriod, boolean[][] currentRecoveredSchedule) { + + Stack stackRecoveringActionsOfFullTimeEmployees = new Stack<>(); + for (int employee = 0 ; employee < this.maxFullTimeEmployee ; employee++) { + boolean[] currentEmployeeSchedule = currentRecoveredSchedule[employee]; + if ( isFullTimeEmployeeActive(employee) && isFullTimeEmployeeAvailableForAbsenceRecovering(employee, workPeriod, currentEmployeeSchedule) ){ + RecoveringActionFullTimeEmployee recoveringAction = + new RecoveringActionFullTimeEmployee(employee, workPeriod, currentEmployeeSchedule); + stackRecoveringActionsOfFullTimeEmployees.push(recoveringAction); + } + } + return stackRecoveringActionsOfFullTimeEmployees; } - private boolean isValidPartTimeEmployeeWorkPeriod(int employee, int workPeriod){ - return isValidWorkingPeriodsOfPartTimeEmployee(employee) && - isValidConsecutiveWorkingPeriodsOfPartTimeEmployee(employee, workPeriod) && - isValidConsecutiveNonWorkingPeriodsBetweenShiftWorksOfPartTimeEmployee(employee, workPeriod); + private boolean isPartTimeEmployeeAvailableForAbsenceRecovering(int employee, int workPeriod, boolean[] currentEmployeeSchedule){ + return !currentEmployeeSchedule[workPeriod] && !isPartTimeEmployeeAbsent(employee, workPeriod) && isValidPartTimeEmployeeWorkPeriod(currentEmployeeSchedule, workPeriod); } - private boolean isValidWorkingPeriodsOfPartTimeEmployee(int employee){ - return this.workingPeriodsPerPartTimeEmployees[employee] < myModelInitialSchedules.myScheduleParameters.maxWorkingPeriodsOfPartTimeEmployeesPerSchedule; + private boolean isValidPartTimeEmployeeWorkPeriod(boolean[] currentEmployeeSchedule, int workPeriod){ + return isValidWorkingPeriodsOfPartTimeEmployeePerSchedule(currentEmployeeSchedule) && + isValidConsecutiveWorkingPeriodsOfPartTimeEmployee(currentEmployeeSchedule, workPeriod) && + isValidConsecutiveNonWorkingPeriodsBetweenShiftWorksOfPartTimeEmployee(currentEmployeeSchedule, workPeriod); + } + + private boolean isValidWorkingPeriodsOfPartTimeEmployeePerSchedule(boolean[] currentEmployeeSchedule){ + int employeeWorkingPeriods = 0; + for ( int workPeriod = 0 ; workPeriod < this.workPeriodsPerSchedule ; workPeriod++ ) { + if (currentEmployeeSchedule[workPeriod]){ + employeeWorkingPeriods +=1 ; + } + } + return employeeWorkingPeriods < myModelInitialSchedules.myScheduleParameters.getMaxWorkingPeriodsOfPartTimeEmployeesPerSchedule(); } - private boolean isValidConsecutiveWorkingPeriodsOfPartTimeEmployee(int employee, int workPeriod){ + private boolean isValidConsecutiveWorkingPeriodsOfPartTimeEmployee(boolean[] currentEmployeeSchedule, int workPeriod){ int consecutiveWorkingPeriods = 1; int compteurWorkPeriod = workPeriod - 1; - while ( compteurWorkPeriod >= 0 && isPartTimeEmployeeWorking(employee, compteurWorkPeriod) ){ + while ( compteurWorkPeriod >= 0 && currentEmployeeSchedule[compteurWorkPeriod] ){ consecutiveWorkingPeriods += 1; compteurWorkPeriod--; } compteurWorkPeriod = workPeriod + 1; - while ( compteurWorkPeriod < this.workPeriodsPerSchedule && isPartTimeEmployeeWorking(employee, compteurWorkPeriod) ){ + while ( compteurWorkPeriod < this.workPeriodsPerSchedule && currentEmployeeSchedule[compteurWorkPeriod] ){ consecutiveWorkingPeriods += 1; compteurWorkPeriod++; } - return consecutiveWorkingPeriods <= myModelInitialSchedules.myScheduleParameters.maxConsecutiveWorkingPeriodsOfPartTimeEmployeesPerShiftWork; + return consecutiveWorkingPeriods <= myModelInitialSchedules.myScheduleParameters.getMaxConsecutiveWorkingPeriodsOfPartTimeEmployeesPerShiftWork(); } - private boolean isValidConsecutiveNonWorkingPeriodsBetweenShiftWorksOfPartTimeEmployee(int employee, int workPeriod){ + private boolean isValidConsecutiveNonWorkingPeriodsBetweenShiftWorksOfPartTimeEmployee(boolean[] currentEmployeeSchedule, int workPeriod){ int consecutivePreviousNonWorkingPeriods = 0; int compteurWorkPeriod = workPeriod - 1; - while ( compteurWorkPeriod >= 0 && !isPartTimeEmployeeWorking(employee, compteurWorkPeriod) ){ + while ( compteurWorkPeriod >= 0 && !currentEmployeeSchedule[compteurWorkPeriod] ){ consecutivePreviousNonWorkingPeriods += 1; compteurWorkPeriod--; } @@ -93,12 +173,12 @@ public class RecoveredSchedulesArray extends AbsenceSchedulesArray{ validConsecutivePreviousNonWorkingPeriods = true; } else if (consecutivePreviousNonWorkingPeriods == 0) { validConsecutivePreviousNonWorkingPeriods = true; - } else if (consecutivePreviousNonWorkingPeriods >= myModelInitialSchedules.myScheduleParameters.minConsecutiveNonWorkingPeriodsBetweenShiftWorksOfPartTimeEmployees) { + } else if (consecutivePreviousNonWorkingPeriods >= myModelInitialSchedules.myScheduleParameters.getMinConsecutiveNonWorkingPeriodsBetweenShiftWorksOfPartTimeEmployees()) { validConsecutivePreviousNonWorkingPeriods = true; } int consecutiveNextNonWorkingPeriods = 0; compteurWorkPeriod = workPeriod + 1; - while ( compteurWorkPeriod < this.workPeriodsPerSchedule && !isPartTimeEmployeeWorking(employee, compteurWorkPeriod) ){ + while ( compteurWorkPeriod < this.workPeriodsPerSchedule && !currentEmployeeSchedule[compteurWorkPeriod] ){ consecutiveNextNonWorkingPeriods += 1; compteurWorkPeriod++; } @@ -107,46 +187,52 @@ public class RecoveredSchedulesArray extends AbsenceSchedulesArray{ validConsecutiveNextNonWorkingPeriods = true; } else if (consecutiveNextNonWorkingPeriods == 0) { validConsecutiveNextNonWorkingPeriods = true; - } else if (consecutiveNextNonWorkingPeriods >= myModelInitialSchedules.myScheduleParameters.minConsecutiveNonWorkingPeriodsBetweenShiftWorksOfPartTimeEmployees) { + } else if (consecutiveNextNonWorkingPeriods >= myModelInitialSchedules.myScheduleParameters.getMinConsecutiveNonWorkingPeriodsBetweenShiftWorksOfPartTimeEmployees()) { validConsecutiveNextNonWorkingPeriods = true; } return validConsecutivePreviousNonWorkingPeriods && validConsecutiveNextNonWorkingPeriods; } - private boolean isFullTimeEmployeeAvailableForAbsenceRecovering(int employee, int workPeriod){ - return isFullTimeEmployeeActive(employee) && !isFullTimeEmployeeAbsent(employee, workPeriod) && isValidFullTimeEmployeeWorkPeriod(employee,workPeriod); + private boolean isFullTimeEmployeeAvailableForAbsenceRecovering(int employee, int workPeriod, boolean[] currentEmployeeSchedule){ + return !currentEmployeeSchedule[workPeriod] && !isFullTimeEmployeeAbsent(employee, workPeriod) && isValidFullTimeEmployeeWorkPeriod(currentEmployeeSchedule, workPeriod); } - private boolean isValidFullTimeEmployeeWorkPeriod(int employee, int workPeriod){ - return isValidWorkingPeriodsOfFullTimeEmployee(employee) && - isValidConsecutiveWorkingPeriodsOfFullTimeEmployee(employee, workPeriod) && - isValidConsecutiveNonWorkingPeriodsBetweenShiftWorksOfFullTimeEmployee(employee, workPeriod); + private boolean isValidFullTimeEmployeeWorkPeriod(boolean[] currentEmployeeSchedule, int workPeriod){ + return isValidWorkingPeriodsOfFullTimeEmployeePerSchedule(currentEmployeeSchedule) && + isValidConsecutiveWorkingPeriodsOfFullTimeEmployee(currentEmployeeSchedule, workPeriod) && + isValidConsecutiveNonWorkingPeriodsBetweenShiftWorksOfFullTimeEmployee(currentEmployeeSchedule, workPeriod); } - private boolean isValidWorkingPeriodsOfFullTimeEmployee(int employee){ - return this.workingPeriodsPerFullTimeEmployees[employee] < myModelInitialSchedules.myScheduleParameters.maxWorkingPeriodsOfFullTimeEmployeesPerSchedule; + private boolean isValidWorkingPeriodsOfFullTimeEmployeePerSchedule(boolean[] currentEmployeeSchedule){ + int employeeWorkingPeriods = 0; + for ( int workPeriod = 0 ; workPeriod < this.workPeriodsPerSchedule ; workPeriod++ ) { + if (currentEmployeeSchedule[workPeriod]){ + employeeWorkingPeriods +=1 ; + } + } + return employeeWorkingPeriods < myModelInitialSchedules.myScheduleParameters.getMaxWorkingPeriodsOfFullTimeEmployeesPerSchedule(); } - private boolean isValidConsecutiveWorkingPeriodsOfFullTimeEmployee(int employee, int workPeriod){ + private boolean isValidConsecutiveWorkingPeriodsOfFullTimeEmployee(boolean[] currentEmployeeSchedule, int workPeriod){ int consecutiveWorkingPeriods = 1; int compteurWorkPeriod = workPeriod - 1; - while ( compteurWorkPeriod >= 0 && isFullTimeEmployeeWorking(employee, compteurWorkPeriod) ){ + while ( compteurWorkPeriod >= 0 && currentEmployeeSchedule[compteurWorkPeriod] ){ consecutiveWorkingPeriods += 1; compteurWorkPeriod--; } compteurWorkPeriod = workPeriod + 1; - while ( compteurWorkPeriod < this.workPeriodsPerSchedule && isFullTimeEmployeeWorking(employee, compteurWorkPeriod) ){ + while ( compteurWorkPeriod < this.workPeriodsPerSchedule && currentEmployeeSchedule[compteurWorkPeriod] ){ consecutiveWorkingPeriods += 1; compteurWorkPeriod++; } - return consecutiveWorkingPeriods <= myModelInitialSchedules.myScheduleParameters.maxConsecutiveWorkingPeriodsOfFullTimeEmployeesPerShiftWork; + return consecutiveWorkingPeriods <= myModelInitialSchedules.myScheduleParameters.getMaxConsecutiveWorkingPeriodsOfFullTimeEmployeesPerShiftWork(); } - private boolean isValidConsecutiveNonWorkingPeriodsBetweenShiftWorksOfFullTimeEmployee(int employee, int workPeriod){ + private boolean isValidConsecutiveNonWorkingPeriodsBetweenShiftWorksOfFullTimeEmployee(boolean[] currentEmployeeSchedule, int workPeriod){ int consecutivePreviousNonWorkingPeriods = 0; int compteurWorkPeriod = workPeriod - 1; - while ( compteurWorkPeriod >= 0 && !isFullTimeEmployeeWorking(employee, compteurWorkPeriod)){ + while ( compteurWorkPeriod >= 0 && !currentEmployeeSchedule[compteurWorkPeriod] ){ consecutivePreviousNonWorkingPeriods += 1; compteurWorkPeriod--; } @@ -155,12 +241,12 @@ public class RecoveredSchedulesArray extends AbsenceSchedulesArray{ validConsecutivePreviousNonWorkingPeriods = true; } else if (consecutivePreviousNonWorkingPeriods == 0) { validConsecutivePreviousNonWorkingPeriods = true; - } else if (consecutivePreviousNonWorkingPeriods >= myModelInitialSchedules.myScheduleParameters.minConsecutiveNonWorkingPeriodsBetweenShiftWorksOfFullTimeEmployees) { + } else if (consecutivePreviousNonWorkingPeriods >= myModelInitialSchedules.myScheduleParameters.getMinConsecutiveNonWorkingPeriodsBetweenShiftWorksOfFullTimeEmployees()) { validConsecutivePreviousNonWorkingPeriods = true; } int consecutiveNextNonWorkingPeriods = 0; compteurWorkPeriod = workPeriod + 1; - while ( compteurWorkPeriod < this.workPeriodsPerSchedule && !isFullTimeEmployeeWorking(employee, compteurWorkPeriod) ){ + while ( compteurWorkPeriod < this.workPeriodsPerSchedule && !currentEmployeeSchedule[compteurWorkPeriod] ){ consecutiveNextNonWorkingPeriods += 1; compteurWorkPeriod++; } @@ -169,7 +255,7 @@ public class RecoveredSchedulesArray extends AbsenceSchedulesArray{ validConsecutiveNextNonWorkingPeriods = true; } else if (consecutiveNextNonWorkingPeriods == 0) { validConsecutiveNextNonWorkingPeriods = true; - } else if (consecutiveNextNonWorkingPeriods >= myModelInitialSchedules.myScheduleParameters.minConsecutiveNonWorkingPeriodsBetweenShiftWorksOfFullTimeEmployees) { + } else if (consecutiveNextNonWorkingPeriods >= myModelInitialSchedules.myScheduleParameters.getMinConsecutiveNonWorkingPeriodsBetweenShiftWorksOfFullTimeEmployees()) { validConsecutiveNextNonWorkingPeriods = true; } return validConsecutivePreviousNonWorkingPeriods && validConsecutiveNextNonWorkingPeriods; diff --git a/Travail_de_session/SchedulesRecovery/RecoveringAction.java b/Travail_de_session/SchedulesRecovery/RecoveringAction.java new file mode 100644 index 0000000..48ff6c9 --- /dev/null +++ b/Travail_de_session/SchedulesRecovery/RecoveringAction.java @@ -0,0 +1,33 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package SchedulesRecovery; + +/** + * + * @author frabe + */ +public class RecoveringAction { + public int employee; + public int workPeriod; + public boolean[] employeeScheduleBeforeRecoveringAction; + public boolean[] employeeScheduleAfterRecoveringAction; + + public RecoveringAction( int employee, int workPeriod, boolean[] employeeSchedule){ + this.employee = employee; + this.workPeriod = workPeriod; + this.employeeScheduleBeforeRecoveringAction = new boolean[employeeSchedule.length]; + this.employeeScheduleAfterRecoveringAction = new boolean[employeeSchedule.length]; + for (int i = 0 ; i < employeeSchedule.length ; i++) { + this.employeeScheduleBeforeRecoveringAction[i] = employeeSchedule[i]; + if (i == workPeriod) { + this.employeeScheduleAfterRecoveringAction[i] = true; + } else { + this.employeeScheduleAfterRecoveringAction[i] = employeeSchedule[i]; + } + } + } + +} diff --git a/Travail_de_session/SchedulesRecovery/RecoveringActionFullTimeEmployee.java b/Travail_de_session/SchedulesRecovery/RecoveringActionFullTimeEmployee.java new file mode 100644 index 0000000..5a4ac25 --- /dev/null +++ b/Travail_de_session/SchedulesRecovery/RecoveringActionFullTimeEmployee.java @@ -0,0 +1,18 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package SchedulesRecovery; + +/** + * + * @author frabe + */ +public class RecoveringActionFullTimeEmployee extends RecoveringAction{ + + public RecoveringActionFullTimeEmployee( int employee, int workPeriod, boolean[] employeeSchedule){ + super(employee, workPeriod, employeeSchedule); + } + +} diff --git a/Travail_de_session/SchedulesRecovery/RecoveringActionPartTimeEmployee.java b/Travail_de_session/SchedulesRecovery/RecoveringActionPartTimeEmployee.java new file mode 100644 index 0000000..4411ee8 --- /dev/null +++ b/Travail_de_session/SchedulesRecovery/RecoveringActionPartTimeEmployee.java @@ -0,0 +1,19 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package SchedulesRecovery; + +/** + * + * @author frabe + */ +public class RecoveringActionPartTimeEmployee extends RecoveringAction{ + + public RecoveringActionPartTimeEmployee( int employee, int workPeriod, boolean[] employeeSchedule){ + super(employee, workPeriod, employeeSchedule); + } + +} + diff --git a/Travail_de_session/SchedulesRecovery/ScheduleStateComparator.java b/Travail_de_session/SchedulesRecovery/ScheduleStateComparator.java new file mode 100644 index 0000000..a8c81e3 --- /dev/null +++ b/Travail_de_session/SchedulesRecovery/ScheduleStateComparator.java @@ -0,0 +1,19 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package SchedulesRecovery; + +import java.util.Comparator; +import org.javatuples.Triplet; + +/** + * + * @author frabe + */ +public class ScheduleStateComparator implements Comparator< Triplet > { + public int compare(Triplet o1, Triplet o2) { + return o1.getValue2().compareTo(o2.getValue2()); + } +} diff --git a/Travail_de_session/javatuples-1.2.jar b/Travail_de_session/javatuples-1.2.jar new file mode 100644 index 0000000000000000000000000000000000000000..8944308f1460dd6697759ef50ec2443f753d3430 GIT binary patch literal 65508 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

-1D_Is)*?yo4i)0(PamSvf=m?I-Qs723CpVzO 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