Le Bureau de la stratégie d'IA, auquel j'appartiens, organise une "Réunion de consultation sur les problèmes d'IA" en interne pour rechercher des solutions en combinant des outils et des bibliothèques existants pour répondre aux besoins de "Vous ne pouvez pas faire cela?" Nous faisons également des affaires.
Par exemple, il existe un service appelé LIFULL HOME'S Living Counter. Il s'agit d'un service qui vous permet de consulter gratuitement un conseiller dédié à la recherche de logements en matière de construction et de recherche de logements.
Est-il possible d'automatiser et d'optimiser le mécanisme de création d'un décalage pour ce conseiller en recherche de logement? Aujourd'hui, je voudrais vous présenter l'une des façons d'utiliser Google OR tools que j'ai trouvée là-bas.
Ces problèmes de planification peuvent parfois être résolus sous forme de problèmes de respect des contraintes (CSP).
Certaines conditions doivent être remplies, mais elles sont importantes
Si vous pouvez y penser séparément, vous pouvez le résoudre à l'aide d'un solveur.
Tout ce que vous avez à faire est de demander au solveur de calculer la solution qui satisfait les conditions de contrainte, puis de spécifier les conditions que vous souhaitez optimiser. ici,
Comme condition de contrainte
Optimisons la valeur.
Tout d'abord, préparez un modèle du solveur. Ici, nous utilisons le modèle CP-SAT. Consultez https://developers.google.com/optimization/cp/cp_solver pour plus de détails.
from ortools.sat.python import cp_model
model = cp_model.CpModel()
Ensuite, préparez une variable à utiliser pour l'indicateur de planification.
Par exemple, si vous effectuez un décalage de s frame pendant d jours pour n conseillers, vous pouvez créer le tableau de décalage suivant.
Afin de sortir le tableau ci-dessus comme résultat de calcul, si vous le décomposez comme suit et en faites un "tableau tridimensionnel" de n, d, s
Peut être considéré comme.
En d'autres termes, la question de savoir où placer le drapeau dans le «tableau tridimensionnel» de «n, d, s» a été réduite.
Définissons une constante selon cet exemple.
num_advisers = 5
num_shifts = 3
days = ["journée", "Mois", "Feu", "eau", "bois", "Argent", "sol"]
num_days = len(days)
adviser_name = ["Peut", "Ichihana", "Nino", "Sanku", "Yotsuba"]
all_advisers = range(num_advisers)
all_shifts = range(num_shifts)
all_days = range(num_days)
La variable pour définir l'indicateur 0 ou 1 et demander au solveur de la résoudre est créée en tant qu'instance de la classe `` ortools.sat.python.cp_model.IntVar '', elle ressemble donc à un tableau pseudo multidimensionnel utilisant dict. Je vais préparer quelque chose qui peut être utilisé.
shifts = {}
for n in all_advisers:
for d in all_days:
for s in all_shifts:
shifts[(n, d, s)] = model.NewBoolVar('shift_n%id%is%i' % (n, d, s))
Les contraintes sont saisies avec model.Add ()
.
** Deux personnes ne peuvent pas être dans le même quart de travail en même temps: ** Assurez-vous qu'un seul drapeau est défini lorsque le diagramme "3D array" est visualisé depuis "top".
for d in all_days:
for s in all_shifts:
model.Add(sum(shifts[(n, d, s)] for n in all_advisers) == 1)
** Les conseillers ne peuvent changer qu'un seul créneau par jour: ** Assurez-vous qu'un seul indicateur est défini lorsque le diagramme "tableau tridimensionnel" est visualisé depuis le "front".
for n in all_advisers:
for d in all_days:
model.Add(sum(shifts[(n, d, s)] for s in all_shifts) <= 1)
** Évitez autant que possible les biais temporels: **
Puisqu'il y a un cadre 3 × 7 = 21
, définissez le cadre 21 // 5 = 4
comme limite inférieure,
Il semble qu'il n'y aura pas de biais si le cadre (21 // 5) + 1 = 5
est défini comme limite supérieure pour chaque conseiller.
Dans le diagramme "3D Array", ajoutez tous les indicateurs de la table du plan pour chaque conseiller afin que le résultat soit supérieur ou égal à la limite inférieure et inférieur ou égal à la limite supérieure.
min_shifts_per_adviser = (num_shifts * num_days) // num_advisers
max_shifts_per_adviser = min_shifts_per_adviser + 1
for n in all_advisers:
num_shifts_worked = sum(
shifts[(n, d, s)] for d in all_days for s in all_shifts)
model.Add(min_shifts_per_adviser <= num_shifts_worked)
model.Add(num_shifts_worked <= max_shifts_per_adviser)
Ensuite, quantifiez le désir "d'entrer dans ce quart de travail!" Cela peut être une valeur simple de 0 ou 1, alors préparez-le comme un tableau multidimensionnel avec obéissance.
Si vous décidez, vous pouvez quantifier les souhaits de décalage pour 5 personnes comme le tableau 3D suivant.
shift_requests = [
[[0, 0, 1], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 0, 1]],
[[0, 0, 0], [0, 0, 0], [0, 1, 0], [0, 1, 0], [1, 0, 0], [0, 0, 0], [0, 0, 1]], #Ichihana
[[0, 1, 0], [0, 1, 0], [0, 0, 0], [1, 0, 0], [0, 0, 0], [0, 1, 0], [0, 0, 0]],
[[0, 0, 1], [0, 0, 0], [1, 0, 0], [0, 1, 0], [0, 0, 0], [1, 0, 0], [0, 0, 0]],
[[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 0, 0], [1, 0, 0], [0, 1, 0], [0, 0, 0]]
]
Ici, lorsque shifts contient une valeur, si le calcul de shift_requests [n] [d] [s] * shift [(n, d, s)]
est effectué pour chaque élément,
Le résultat du calcul est 1 uniquement lorsque "l'élément répond aux souhaits du conseiller".
En passant cela à une fonction appelée model.Maximize, vous pouvez indiquer au solveur les conditions d'optimisation.
model.Maximize(
sum(shift_requests[n][d][s] * shifts[(n, d, s)] for n in all_advisers
for d in all_days for s in all_shifts))
https://developers.google.com/optimization/scheduling/employee_scheduling
from ortools.sat.python import cp_model
def main():
# This program tries to find an optimal assignment of advisers to shifts
# (3 shifts per day, for 7 days), subject to some constraints (see below).
# Each adviser can request to be assigned to specific shifts.
# The optimal assignment maximizes the number of fulfilled shift requests.
num_advisers = 5
num_shifts = 3
days = ["journée", "Mois", "Feu", "eau", "bois", "Argent", "sol"]
num_days = len(days)
adviser_name = ["Peut", "Ichihana", "Nino", "Sanku", "Yotsuba"]
all_advisers = range(num_advisers)
all_shifts = range(num_shifts)
all_days = range(num_days)
shift_requests = [
[[0, 0, 1], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 0, 1]],
[[0, 0, 0], [0, 0, 0], [0, 1, 0], [0, 1, 0], [1, 0, 0], [0, 0, 0], [0, 0, 1]],
[[0, 1, 0], [0, 1, 0], [0, 0, 0], [1, 0, 0], [0, 0, 0], [0, 1, 0], [0, 0, 0]],
[[0, 0, 1], [0, 0, 0], [1, 0, 0], [0, 1, 0], [0, 0, 0], [1, 0, 0], [0, 0, 0]],
[[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 0, 0], [1, 0, 0], [0, 1, 0], [0, 0, 0]]
]
# Creates the model.
model = cp_model.CpModel()
# Creates shift variables.
# shifts[(n, d, s)]: adviser 'n' works shift 's' on day 'd'.
shifts = {}
for n in all_advisers:
for d in all_days:
for s in all_shifts:
shifts[(n, d, s)] = model.NewBoolVar('shift_n%id%is%i' % (n, d, s))
# Each shift is assigned to exactly one adviser in .
for d in all_days:
for s in all_shifts:
model.Add(sum(shifts[(n, d, s)] for n in all_advisers) == 1)
# Each adviser works at most one shift per day.
for n in all_advisers:
for d in all_days:
model.Add(sum(shifts[(n, d, s)] for s in all_shifts) <= 1)
# min_shifts_assigned is the largest integer such that every adviser can be
# assigned at least that number of shifts.
min_shifts_per_adviser = (num_shifts * num_days) // num_advisers
max_shifts_per_adviser = min_shifts_per_adviser + 1
for n in all_advisers:
num_shifts_worked = sum(
shifts[(n, d, s)] for d in all_days for s in all_shifts)
model.Add(min_shifts_per_adviser <= num_shifts_worked)
model.Add(num_shifts_worked <= max_shifts_per_adviser)
model.Maximize(
sum(shift_requests[n][d][s] * shifts[(n, d, s)] for n in all_advisers
for d in all_days for s in all_shifts))
# Creates the solver and solve.
solver = cp_model.CpSolver()
solver.Solve(model)
for d in all_days:
print(days[d] + "journée:")
for s in all_shifts:
for n in all_advisers:
if solver.Value(shifts[(n, d, s)]) == 1:
if shift_requests[n][d][s] == 1:
print('Adviser', adviser_name[n], 'works shift', s, '(requested).')
else:
print('Adviser', adviser_name[n], 'works shift', s, '(not requested).')
print()
# Statistics.
print()
print('Statistics')
print(' - Number of shift requests met = %i' % solver.ObjectiveValue(), '(out of', num_advisers * min_shifts_per_adviser, ')')
print(' - wall time : %f s' % solver.WallTime())
if __name__ == '__main__':
main()
dimanche:
Conseiller Yotsuba travaille quart 0(not requested).
Le conseiller Nino travaille le quart de travail 1(requested).
Le conseiller Sanku travaille le quart de travail 2(requested).
Lundi:
Conseiller Ichihana travaille quart de travail 0(not requested).
Le conseiller Nino travaille le quart de travail 1(requested).
Le conseiller Yotsuba travaille en équipe 2(requested).
Mardi:
Le conseiller Sanku travaille quart de travail 0(requested).
Le conseiller Ichihana travaille le quart de travail 1(requested).
Le conseiller Yotsuba travaille en équipe 2(not requested).
Mercredi:
Le conseiller Nino travaille quart de travail 0(requested).
Le conseiller Ichihana travaille le quart de travail 1(requested).
Le conseiller May travaille le quart 2(not requested).
Jeudi:
Conseiller Yotsuba travaille quart 0(requested).
Le conseiller Sanku travaille le quart de travail 1(not requested).
Le conseiller May travaille le quart 2(requested).
Vendredi:
Le conseiller Sanku travaille quart de travail 0(requested).
Le conseiller May travaille le quart 1(requested).
Le conseiller Ichihana travaille en équipe 2(not requested).
samedi:
Le conseiller Nino travaille quart de travail 0(not requested).
Le conseiller Yotsuba travaille le quart de travail 1(not requested).
Le conseiller May travaille le quart 2(requested).
Statistics
- Number of shift requests met = 13 (out of 20 )
- wall time : 0.007237 s
Nous avons pu produire la solution qui répondait le plus aux souhaits de quart de travail du conseiller.
Enjoy!