Je fais face à un problème, je ne sais pas trop comment aborder. Je dois générer un calendrier pour les employés, chacun ayant des contraintes de travail spécifiques (certaines personnelles, d'autres communes)
Avec quoi je travaille:
L'utilisateur en question est la personne qui s'occupe du calendrier, j'essaye de construire une solution qui va générer automatiquement un calendrier qui obéit à toutes les contraintes. La solution est juste une grande entrée de paramètres "ajouter des médecins" et "ajouter des contraintes" pour chaque médecin, puis un bouton "générer un calendrier". C'est vraiment basique pour l'utilisateur.
Mon problème:
Je ne sais pas comment générer la planification réelle, j'ai lu sur les réseaux neuronaux, les algorithmes génétiques, etc., et ils semblent tous être la bonne solution mais pas vraiment.
Quand je regarde les GA, ils sont faits pour trouver une solution avec une population donnée (mon problème), mais la population de départ doit déjà obéir à l'ensemble de contraintes donné, qui serait alors optimisé. Dans ce cas, ma population de départ est déjà la solution. Je n'ai pas besoin qu'il soit "optimisé". Peu importe qu'une seule personne travaille 3 lundi soirs d'affilée, tant que c'est correct et que d'autres travaillent le même montant, cela signifie que d'autres travailleront également 3 lundi soir à un moment donné et ça va. Ce qui me fait penser que les GA sont trop "avancés" pour moi, car mon problème est déjà résolu avec le point de départ d'une GA.
Mais là encore, les GA semblent vraiment vraiment faits pour ça, donc je ne peux pas le comprendre correctement?
Quoi qu'il en soit, comme je n'ai jamais utilisé d'AG (ou de réseaux de neurones, ou quoi que ce soit du genre), je voudrais être sûr de choisir la bonne approche avant de m'engager dans une courbe d'apprentissage comme celle-ci.
Ma question:
Selon vous, quelle est la bonne approche/algorithme/technique pour un problème comme le mien? Gaz? Les réseaux de neurones? Quelque chose de complètement différent?
Je suis à l'écoute et ouvert pour plus de détails si nécessaire, mais je pense que je me suis bien fait comprendre :)
Les algorithmes génétiques et les réseaux de neurones ne conviennent pas ici. Ce sont des méta-heuristiques pour trouver une solution approximative suffisamment bonne à un problème. Notamment, les deux nécessitent que vous trouviez une fonction de coût pour évaluer les solutions candidates. Une fois que vous avez une telle fonction de coût, il pourrait être plus facile de trouver manuellement un algorithme qui optimise ce coût.
C'est une pensée importante: compte tenu de deux horaires, nous avons besoin d'un moyen de décider si l'annexe A ou l'annexe B est "meilleure". Vous avez énuméré divers critères, mais leur relation n'est pas claire. Le non-respect d'un critère échoue-t-il à l'ensemble de la solution? Ou l'échec partiel d'une contrainte en fait-il simplement une pire solution que d'autres?
Au niveau le plus élémentaire, vous pouvez simplement partitionner la semaine en intervalles de temps discrets et forcer toutes les combinaisons slot-docteur. Cependant, vous pouvez utiliser des contraintes qui échouent fortement pour réduire cet espace de recherche à une taille plus gérable. Les restrictions sur le temps de travail et les quarts de nuit semblent convenir à une telle limitation de l'espace de recherche. Vous vous retrouvez alors avec des centaines de solutions candidates.
Pour sélectionner la meilleure solution candidate, vous devrez les classer. C'est assez facile si une contrainte souple a une priorité claire sur toutes les autres contraintes souples, par ex. si un médecin ne peut pas travailler un certain quart de travail, cela est plus important qu'un médecin ne voulant pas travailler ce quart de travail. Mais je ne peux pas décider de ces règles pour vous - c'est une décision de gestion. C'est plus difficile si deux contraintes souples n'ont pas de priorité claire, auquel cas vous devrez trouver une sorte de fonction de coût qui unifie l'importance de deux contraintes dans une seule métrique.
Je construirais probablement un algorithme gourmand qui remplit un calendrier vide en fonction de certains critères de priorité. Ce n'est peut-être pas la solution la plus optimale, mais c'est beaucoup plus facile que de penser à ce que signifie réellement "optimal".
Dans un premier temps, vous pouvez remplir les quarts de nuit le week-end et essayer de sélectionner les médecins qui n'ont pas effectué de quart de nuit le plus longtemps, en tenant également compte des souhaits des utilisateurs "Je ne peux pas travailler là-bas" . En supposant que ces souhaits sont hebdomadaires et non continus, cela signifie qu'un médecin qui ne peut pas travailler les soirs de week-end pendant une semaine serait choisi la semaine prochaine.
Une procédure similaire peut être utilisée pour les autres nuits: après avoir essayé de respecter les souhaits des utilisateurs, vous remplissez des médecins en fonction de qui n'a pas effectué de quarts de nuit depuis le plus longtemps. La procédure se répète de manière similaire pour le troisième type de créneau horaire, le jour change. Si deux souhaits d'utilisateurs ne peuvent pas être conciliés, vous pouvez garder une trace de la fréquence à laquelle un souhait d'utilisateurs a été accordé, puis prioriser le médecin avec moins de souhaits accordés.
Malheureusement, je peux voir quelques façons de jouer à ce système: par exemple si un médecin était choisi pour travailler un quart de nuit le week-end mais présentait une demande "ne peut pas travailler là-bas", son choix serait retardé d'une semaine - ce qui réduirait la fréquence des quarts de nuit du week-end aux frais de ses collègues. Si une procédure de résolution de souhaits est mise en œuvre qui examine le nombre de demandes refusées, un utilisateur peut soumettre quelques demandes impossibles pour augmenter une demande qu'il souhaite traiter. Cependant, en supposant la bonne foi (et la flexibilité pour les médecins de permuter les changements entre eux), un tel algorithme devrait aboutir à une solution suffisamment bonne.
Vous pouvez utiliser recuit simulé .
J'ai fait quelque chose comme ça avant de décrocher mon premier job - voir https://vimeo.com/20610875 (démo à partir de 2:50, algorithme expliqué à partir de 6:15).
Le recuit simulé est un type d'algorithme génétique, et peut-être qu'il n'était pas adapté en théorie (comme @amon le soutient dans sa réponse ), mais cela fonctionnait très bien en pratique, et c'était à peu près la même utilisation cas comme le vôtre.
Le code source est disponible (C #), mais pendant qu'il fonctionne, c'est terrible, je le crains, c'était il y a quelques années et étant un autodidacte, je ne savais rien de la maintenabilité. Cela a cependant donné de très bons résultats.
Comment cela fonctionne en un mot:
Générez 1 horaire possible (ce n'est peut-être pas très bon, mais physiquement possible) comme point de départ. Un algorithme génétique n'est pas nécessaire à ce stade - vous pouvez simplement forcer votre chemin vers la première solution que vous pouvez trouver. J'ai utilisé retour arrière . La complexité de calcul peut être surmontée en résolvant le rota pour chaque jour séparément. S'il n'y a aucune solution (selon le cas), c'est à ce stade que vous la détectez.
Faites un pool de solutions - disons, 100 copies de cette solution d'entrée de gamme pour commencer.
Muter toutes les solutions au hasard: demandez aux médecins d'échanger leurs quarts de travail, de retirer un médecin au hasard de son quart de travail et d'y mettre une personne disponible au hasard, etc.
Évaluez chaque solution avec une fonction fitness qui détermine sa qualité. Un gars travaille plus de nuits qu'un autre? Soustrayez les points de pénalité. Quelqu'un voulait faire lundi mais ils ne le font pas? Soustrayez à nouveau les points de pénalité.
Prenez - disons - les 20 meilleures solutions et copiez chacune d'elles 5 fois, en écrasant les 80 restantes avec elles, les portant ainsi à la prochaine génération. La survie du plus fort.
Rincez et répétez.
Les nombres sont évidemment arbitraires, vous devrez peut-être jouer avec les paramètres pour trouver les paramètres optimaux pour votre scénario.
Quant à la mutation d'une solution, le recuit simulé introduit quelque chose appelé température. Fondamentalement, cela signifie qu'au début, vous devez muter vos solutions assez fort (par exemple, faites toujours 10 tentatives de permutation de changements en une seule fois) et devenez progressivement moins agressif avec les itérations suivantes, de sorte qu'elles deviennent plus un réglage fin (par exemple, vers le bas à seulement 2 tentatives de réglages par génération).
Algorithmes de génétique s'appliquent ici. Au cours de mon programme de premier cycle, un de mes collègues a écrit un article sur votre problème très similaire.
Vous pouvez rechercher Job Shop Scheduling et aussi Open Shop Scheduling ou Flow Shop Scheduling peuvent être des points de départ intéressants
Pour utiliser un algorithme génétique, vous n'avez pas besoin d'une solution parfaite, vous pouvez commencer avec N candidats aléatoires et appliquer une fonction de fitness à chacun d'eux, par exemple:
En générant N candidats vous choisissez les X meilleurs d'entre eux , ce sont eux qui infligeront le moins de contraintes. En travaillant avec eux, croisement et mutation sur plusieurs générations, on peut se retrouver avec une bonne solution.
Après avoir parlé de tout cela, chaque fois que j'utilisais un algorithme génétique qui reposait davantage sur une mutation que sur un croisement, je pouvais développer un recuit simulé qui fonctionnerait beaucoup mieux, avec une mise en œuvre plus facile. Le coût/fitness et la fonction de mutation pour l'algorithme génétique seront probablement très similaires à ceux utilisés dans un recuit simulé. Je commencerais par là, regardez la réponse de @Konrad Morawski
la recherche Google trouve de bons résultats pour Job Shop et GA