Simplification de l'algorithme de recouvrement.

Ajout des sections recouvrement d'horaire et résultats.
This commit is contained in:
Francois Berube\frabe 2018-04-13 17:51:55 -04:00
parent cb8a2f4fd2
commit fe0e3cb3a2
25 changed files with 1607 additions and 57 deletions

View file

@ -0,0 +1,67 @@
\section{Annexe 1 : Horaires initiales générées par le modèle}
\label{sec:AnnexeHoraires}
\pagebreak
\begin{landscape}
\begin{figure}[ht]
\centering
\includegraphics[width=25cm]{Figures/InitialSchedule1.png}
\caption{Horaire initiale ayant un ratio employés temps plein / employés temps partiel de 0.11.}
\label{fig:InitialSchedule1}
\end{figure}
\begin{figure}[ht]
\centering
\includegraphics[width=25cm]{Figures/InitialSchedule2.png}
\caption{Horaire initiale ayant un ratio employés temps plein / employés temps partiel de 0.17.}
\label{fig:InitialSchedule2}
\end{figure}
\begin{figure}[ht]
\centering
\includegraphics[width=25cm]{Figures/InitialSchedule3.png}
\caption{Horaire initiale ayant un ratio employés temps plein / employés temps partiel de 0.22.}
\label{fig:InitialSchedule3}
\end{figure}
\begin{figure}[ht]
\centering
\includegraphics[width=25cm]{Figures/InitialSchedule4.png}
\caption{Horaire initiale ayant un ratio employés temps plein / employés temps partiel de 0.28.}
\label{fig:InitialSchedule4}
\end{figure}
\begin{figure}[ht]
\centering
\includegraphics[width=25cm]{Figures/InitialSchedule5.png}
\caption{Horaire initiale ayant un ratio employés temps plein / employés temps partiel de 0.33.}
\label{fig:InitialSchedule5}
\end{figure}
\begin{figure}[ht]
\centering
\includegraphics[width=25cm]{Figures/InitialSchedule6.png}
\caption{Horaire initiale ayant un ratio employés temps plein / employés temps partiel de 0.39.}
\label{fig:InitialSchedule6}
\end{figure}
\begin{figure}[ht]
\centering
\includegraphics[width=25cm]{Figures/InitialSchedule7.png}
\caption{Horaire initiale ayant un ratio employés temps plein / employés temps partiel de 0.44.}
\label{fig:InitialSchedule7}
\end{figure}
\begin{figure}[ht]
\centering
\includegraphics[width=25cm]{Figures/InitialSchedule8.png}
\caption{Horaire initiale ayant un ratio employés temps plein / employés temps partiel de 0.50.}
\label{fig:InitialSchedule8}
\end{figure}
\end{landscape}
%%% Local Variables:
%%% mode: latex
%%% TeX-master: "rapport_de_recherche"
%%% End:

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

View file

@ -1,3 +1,6 @@
\section{Introduction}
\label{sec:Introduction}
%%% Local Variables:
%%% mode: latex

File diff suppressed because it is too large Load diff

Binary file not shown.

View file

@ -72,6 +72,7 @@
\include{resultats}
\include{conclusion}
\bibliography{bibliographie}
\include{Annexe1}
\end{document}
%%% Local Variables:

View file

@ -1,3 +1,27 @@
\section{Recouvrement des horaires}
\label{sec:Recouvrement}
Une fois les absences générées dans chacune des horaires initiales, un algorithme de recouvrement des horaires est lancé sur chacune des simulations d'absence. Cette étape permettra de trouver des coûts de recouvrement moyens pour chancune des horaires initiales pour trouver laquelle correspond au coût de recouvrement le plus faible. Les règles régissant ce recouvrement sont les suivantes :
\bigskip
\begin{itemize}
\item Le nombre maximal de périodes de travail par quart de travail est de 3, ce qui équivaut à 12 h.
\item Le nombre minimum de périodes non travaillées entre les quarts de travail est de 3, ce qui équivaut à 12 h.
\item Pour chaque horaire, le nombre maximal de périodes de travail après recouvrement des employés à temps partiel est de 16, ce qui équivaut à 64 h.
\item Pour chaque horaire, le nombre maximal de périodes de travail après recouvrement des employés à temps plein est de 30, ce qui équivaut 120 h.
\item Les employés à temps partiel n'ont pas droit au temps supplémentaire lors de leur recouvrement. De plus, ces derniers ne sont pas payés en cas d'absence.
\item Les employés à temps plein ont droit au temps supplémentaire pour chaque recouvrement effectué. De plus, ces derniers sont payés au taux régulier en cas d'absence.
\end{itemize}
\bigskip
Étant donné que chaque recouvrement influence les recouvrements subséquents en raison de ces contraintes, ce problème est NP-complet. Le nombre de noeuds de l'arbre de recherche permettant de trouver le coût de recouvrement minimal varie donc de façon exponentielle avec le nombre d'absences. Pour éviter que le temps de calcul soit trop important pour cette phase de recouvrement, une fouille partielle de l'arbre de recherche est effectuée par des fouilles en profondeur successives. L'algorithme de recouvrement a les caractéristiques suivantes.
\bigskip
\begin{itemize}
\item Les fouilles en profondeur sont guidées par un heuristique basé sur le coût minimum des actions de recouvrement possibles à chacun des noeuds. Si plusieurs actions correspondent à ce coût minimum, cette dernière est choisie de façon aléatoire.
\item Lors de ces fouilles en profondeur, chaque action de recouvrement, caractérisée par un employé donné recouvrant à une période de travail donné, est marquée dans une matrice de noeuds visités afin de ne pas répéter la même fouille 2 fois.
\item Lorsqu'aucune action n'est possible sans que l'horaire ne soit complètement recouverte, on recommence une nouvelle fouille à partir du sommet de l'arbre.
\item Lorsqu'un certain nombre de retours arrières est atteint, on réinitialise la matrice des noeuds visités. L'algorithme peut aussi s'arrêter après un nombre limite de retours arrières lorsqu'aucune solution n'est trouvée.
\end{itemize}
\bigskip
Ainsi, l'horaire de recouvrement obtenue par cet algorithme ne conduit pas au coût minimum dans tous les cas, mais s'approche de ce dernier. Cette façon de procéder a cependant l'avantage d'être extrêmement rapide et permet de compenser ce biais par rapport à l'optimalité, par un plus grand nombre de simulations effectuées.
%%% Local Variables:
%%% mode: latex

View file

@ -1,3 +1,26 @@
\section{Résultats et Discussion}
\label{sec:RésultatsDiscussion}
\subsection{Comparaison des modèles proposés}
\subsection{Résultats de l'étude de cas}
À l'aide du modèle de génération d'horaires en 2 phases, 8 horaires présentant des ratios employés temps plein / employés temps partiel différents ont été obtenus (voir Annexe 1). Puisque les 2 types d'employés ont des contraintes différentes quant au recouvrement de ces derniers, il a été possible de tester l'effet du taux d'absentéisme et de l'augmentation de la demande. L'effet de ces 2 paramètres sur le coût de recouvrement des 8 horaires sont présentés au Tableau \ref{table:TableauEffetsParametres}. Ces coûts de recouvrement sont présentés comme un ratio par rapport au coût de l'horaire optimale, soit celle ayant un nombre d'employé temps plein maximal.
\begin{figure}[ht]
\centering
\includegraphics[width=16cm]{Figures/TableaueffetsParametres.png}
\caption{Effet des probabilités d'absentéisme et d'augmentation de la demande de travail sur le coût moyen de recouvrement des horaires.}
\label{table:TableauEffetsParametres}
\end{figure}
Comme le montre ce tableau, pour une probabilité d'absentéisme nulle (sans absence), le coût des horaires augmente en fonction du nombre d'employé à temps partiel. Cela est dû au fait que ces employés ont un taux horaire régulier 20\% plus élevé que celui des employés à temps plein. De plus, pour tous les ratios de type employés, l'augmentation de la probabilité d'absentéisme et l'augmentation de la probabilité d'augmentation de la demande de travail sont associées à l'augmentation du coût de recouvrement des horaires. Cette augmentation de coûts est liée au temps supplémentatire des employés à temps plein (+50\%) ainsi que du taux horaire régulier supérieur des employés à temps partiel, lesquels sont privilégiés pour effectuer les recouvrements. Enfin, ces résultats démontrent que le ratio de type d'employés qui conduit à l'horaire avec un coût de recouvrement minimal varie selon les deux paramètres testés. En effet, lorsque le nombre d'absence et/ou la demande de travail supplémentaire augmente de façon importante, un nombre d'employés à temps partiel supérieur par rapport au nombre d'employés à temps plein permet d'obtenir un horaire au coût de recouvrement le plus faible. La prinpale raison est un diminution du temps supplémentaire effectué par les employés à temps plein. La Figure xx montre les ratios de type d'employés à privilégier pour la construction d'horaires en fonction des probabilités d'absence et d'augmentation de la demande de travail.
\begin{figure}[ht]
\centering
\includegraphics[width=16cm]{Figures/Mapping.png}
\caption{Proportion d'employés à temps plein optimale en fonction des probabilités d'absentéisme et d'augmentation de la demande de travail.}
\label{table:MappingRatioOptimal}
\end{figure}
%%% Local Variables:
%%% mode: latex

View file

@ -10,7 +10,7 @@ package AbsenceSchedules;
* @author frabe
*/
public class ParametersAbsencesSimulator {
public static double probPresence = 0.9;
public static double probPresence = 0.99;
public static final double probReturn = 0.50;
public static int numberAbsenceSimulations = 1000;
}

View file

@ -24,29 +24,29 @@ public class RecoveredSchedulesArray extends SchedulesArray{
}
private void recoverAbsenceScheduleOptimally() {
boolean[][] initialRecoveredScheduleOfPartTimeEmployees = this.getDeepCopyEmployeesSchedules(this.absenceSchedulesArray.partTimeSchedules);
boolean[][] initialRecoveredScheduleOfFullTimeEmployees = this.getDeepCopyEmployeesSchedules(this.absenceSchedulesArray.fullTimeSchedules);
boolean[][] currentRecoveredScheduleOfPartTimeEmployees = this.getDeepCopyEmployeesSchedules(this.absenceSchedulesArray.partTimeSchedules);
boolean[][] currentRecoveredScheduleOfFullTimeEmployees = this.getDeepCopyEmployeesSchedules(this.absenceSchedulesArray.fullTimeSchedules);
boolean[][] currentRecoveredScheduleOfFullTimeEmployees = this.getDeepCopyEmployeesSchedules(this.absenceSchedulesArray.fullTimeSchedules);
int remainingRecoveringAction = this.absenceSchedulesArray.numberOfRecoveringActionsToPerform;
int initialRemainingRecoveringAction = this.absenceSchedulesArray.numberOfRecoveringActionsToPerform;
int currentRemainingRecoveringAction = this.absenceSchedulesArray.numberOfRecoveringActionsToPerform;
boolean[][] alreadyRecoveredWorkPeriodByPartTimeEmployees = new boolean[this.getNumberPartTimeEmployee()][remainingRecoveringAction];
boolean[][] alreadyRecoveredWorkPeriodByFullTimeEmployees = new boolean[this.getNumberFullTimeEmployee()][remainingRecoveringAction];
boolean[][] alreadyRecoveredWorkPeriodByPartTimeEmployees = new boolean[this.getNumberPartTimeEmployee()][initialRemainingRecoveringAction];
boolean[][] alreadyRecoveredWorkPeriodByFullTimeEmployees = new boolean[this.getNumberFullTimeEmployee()][initialRemainingRecoveringAction];
initializeRecoveredWorkPeriodMatrix (alreadyRecoveredWorkPeriodByPartTimeEmployees, alreadyRecoveredWorkPeriodByFullTimeEmployees);
RecoveringActionComparator recoveringActionCostComparator = new RecoveringActionComparator();
ScheduleState initialScheduleState = new ScheduleState(currentRecoveredScheduleOfPartTimeEmployees, currentRecoveredScheduleOfFullTimeEmployees,
remainingRecoveringAction);
ScheduleState currentScheduleState = null;
boolean isSolutionFound = false;
boolean isAbsenceFound = remainingRecoveringAction != 0;
boolean isAbsenceFound = initialRemainingRecoveringAction != 0;
int numberBackTrack = 0;
int numberBackTrack = 1;
// Fouilles en profondeur en privilegiant les actions avec cout minimum. On marque les noeuds dejà visites pour avoir des fouilles differentes
while ( !isSolutionFound && isAbsenceFound) {
while ( !isSolutionFound && isAbsenceFound ) {
PriorityQueue<RecoveringAction> recoveringActionsOrderedByCost =
getPossibleRecoveringActions(currentRecoveredScheduleOfPartTimeEmployees, currentRecoveredScheduleOfFullTimeEmployees,
alreadyRecoveredWorkPeriodByPartTimeEmployees, alreadyRecoveredWorkPeriodByFullTimeEmployees, remainingRecoveringAction, recoveringActionCostComparator);
alreadyRecoveredWorkPeriodByPartTimeEmployees, alreadyRecoveredWorkPeriodByFullTimeEmployees, currentRemainingRecoveringAction, recoveringActionCostComparator);
if (recoveringActionsOrderedByCost.size() > 0){
RecoveringAction currentRecoveringAction;
if (recoveringActionsOrderedByCost.size() == 1){
@ -56,21 +56,17 @@ public class RecoveredSchedulesArray extends SchedulesArray{
currentRecoveringAction = chooseMinimalCostRecoveringActionRandomly(recoveringActionsOrderedByCost);
}
boolean[][] recoveredScheduleOfPartTimeEmployeesAfterAction = this.getDeepCopyEmployeesSchedules(currentRecoveredScheduleOfPartTimeEmployees);
boolean[][] recoveredScheduleOfFullTimeEmployeesAfterAction = this.getDeepCopyEmployeesSchedules(currentRecoveredScheduleOfFullTimeEmployees);
if (currentRecoveringAction.getClass().getName().equals("SchedulesRecovery.RecoveringActionPartTimeEmployee")) {
recoveredScheduleOfPartTimeEmployeesAfterAction[currentRecoveringAction.employee][currentRecoveringAction.workPeriod] = true;
alreadyRecoveredWorkPeriodByPartTimeEmployees[currentRecoveringAction.employee][remainingRecoveringAction-1] = true;
currentRecoveredScheduleOfPartTimeEmployees[currentRecoveringAction.employee][currentRecoveringAction.workPeriod] = true;
alreadyRecoveredWorkPeriodByPartTimeEmployees[currentRecoveringAction.employee][currentRemainingRecoveringAction-1] = true;
} else if (currentRecoveringAction.getClass().getName().equals("SchedulesRecovery.RecoveringActionFullTimeEmployee")) {
recoveredScheduleOfFullTimeEmployeesAfterAction[currentRecoveringAction.employee][currentRecoveringAction.workPeriod] = true;
alreadyRecoveredWorkPeriodByPartTimeEmployees[currentRecoveringAction.employee][remainingRecoveringAction-1] = true;
currentRecoveredScheduleOfFullTimeEmployees[currentRecoveringAction.employee][currentRecoveringAction.workPeriod] = true;
alreadyRecoveredWorkPeriodByPartTimeEmployees[currentRecoveringAction.employee][currentRemainingRecoveringAction-1] = true;
}
currentScheduleState = new ScheduleState(recoveredScheduleOfPartTimeEmployeesAfterAction, recoveredScheduleOfFullTimeEmployeesAfterAction,
remainingRecoveringAction - 1);
currentRemainingRecoveringAction--;
} else if (numberBackTrack > this.getNumberFullTimeEmployee() + this.getNumberPartTimeEmployee()) {
} else if (numberBackTrack % (this.getNumberFullTimeEmployee() + this.getNumberPartTimeEmployee()) == 0) {
//Si le nombre de retour au sommet atteint le nombre total d'employes, on reinitialize les matrices de noeuds visites.
initializeRecoveredWorkPeriodMatrix (alreadyRecoveredWorkPeriodByPartTimeEmployees, alreadyRecoveredWorkPeriodByFullTimeEmployees);
} else if (numberBackTrack > 10000) {
@ -79,27 +75,22 @@ public class RecoveredSchedulesArray extends SchedulesArray{
break;
} else{
// Lorsqu'on est bloque dans l'arbre de recherche, on revient au sommet de l'arbre et on recommence une nouvelle fouille.
currentScheduleState = initialScheduleState;
remainingRecoveringAction = currentScheduleState.remainingRecoveringAction;
currentRemainingRecoveringAction = initialRemainingRecoveringAction;
currentRecoveredScheduleOfPartTimeEmployees = this.getDeepCopyEmployeesSchedules(initialRecoveredScheduleOfPartTimeEmployees);
currentRecoveredScheduleOfFullTimeEmployees = this.getDeepCopyEmployeesSchedules(initialRecoveredScheduleOfFullTimeEmployees);
numberBackTrack++;
}
if (remainingRecoveringAction - 1 == 0){
if (currentRemainingRecoveringAction == 0){
isSolutionFound = true;
}
currentRecoveredScheduleOfPartTimeEmployees = currentScheduleState.currentRecoveredScheduleOfPartTimeEmployees;
currentRecoveredScheduleOfFullTimeEmployees = currentScheduleState.currentRecoveredScheduleOfFullTimeEmployees;
remainingRecoveringAction = currentScheduleState.remainingRecoveringAction;
}
if (isSolutionFound || !isAbsenceFound) {
this.fullTimeSchedules = currentRecoveredScheduleOfFullTimeEmployees;
this.partTimeSchedules = currentRecoveredScheduleOfPartTimeEmployees;
this.totalScheduleCost = EmployeeCostCalculator.getFullRecoveredScheduleCost(this, myParametersInitialSchedules);
} else {
this.totalScheduleCost = Integer.MAX_VALUE;
}

View file

@ -1,26 +0,0 @@
/*
* 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 ScheduleState {
boolean[][] currentRecoveredScheduleOfPartTimeEmployees;
boolean[][] currentRecoveredScheduleOfFullTimeEmployees;
int remainingRecoveringAction;
public ScheduleState(boolean[][] currentRecoveredScheduleOfPartTimeEmployees, boolean[][] currentRecoveredScheduleOfFullTimeEmployees,
int remainingRecoveringAction) {
this.currentRecoveredScheduleOfPartTimeEmployees = currentRecoveredScheduleOfPartTimeEmployees;
this.currentRecoveredScheduleOfFullTimeEmployees = currentRecoveredScheduleOfFullTimeEmployees;
this.remainingRecoveringAction = remainingRecoveringAction;
}
}