-
Notifications
You must be signed in to change notification settings - Fork 0
/
funcionesConstructivoPark.py
262 lines (205 loc) · 12.9 KB
/
funcionesConstructivoPark.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
#Constructivo (Park, 2001) adaptado a Rostering -> Rotación de personal
#----------------------------------------------------------------------
#Librerías
#---------
import cargaInstancias #Librería con funcionalidades desarrolladas para gestión de las instancias
import json
import pprint as pp
import random
#Definición de funciones específicas del constructivo
#----------------------------------------------------
#Función para inicializar la programación del empleado dependiendo de la instancia
def generarProgramacionEmpleado(horizonteTiempo, tiposTurno):
#Inicializar el diccionario contenedor
programacionEmpleado = dict()
#Inicializar valores
programacionEmpleado['cerrada'] = False
#Forma resumida -> genera problemas por enlazado o referencia de las listas
#programacionEmpleado['itinerario'] = [ [ ] ] * len(horizonteTiempo)
#Forma explícita de agregar diccionarios vacíos de turnos en el listado de días del cuadro de turnos del empleado
programacionEmpleado['itinerario'] = list()
[programacionEmpleado['itinerario'].append(dict()) for _ in range(len(horizonteTiempo))]
programacionEmpleado['totalDiasLibre'] = len(horizonteTiempo)
programacionEmpleado['diasTrabajo'] = 0
programacionEmpleado['conteoTiposTurno'] = dict()
for tipoTurno in tiposTurno:
programacionEmpleado['conteoTiposTurno'][tipoTurno] = 0
programacionEmpleado['diasLibreConsecutivos'] = len(horizonteTiempo)
programacionEmpleado['diasTrabajandoConsecutivos'] = 0
#Retornar una copia (evitar paso por referencia por defecto de Python)
return programacionEmpleado.copy()
#Función para obtener el número de días consecutivos libres de un itinerario
def numeroDiasLibresConsecutivos(programacionEnRevision, horizonteTiempo):
#Estructura para acumular el tamaño de las secuencias con días libres consecutivos
tamaniosSecuenciasLibres = []
#Inicializar la longitud de la secuencia actual
longitudSecuenciaActualLibres = 0
#Recorrer todo el horizonte de tiempo
for i in range(horizonteTiempo):
#Si el día actual del empleado está libre
if programacionEnRevision['itinerario'][i] == dict():
longitudSecuenciaActualLibres += 1
else:#El día no está libre, se rompe la secuencia
#Acumular el tamaño de secuencia (si es diferente de cero)
if longitudSecuenciaActualLibres != 0:
tamaniosSecuenciasLibres.append(longitudSecuenciaActualLibres)
#Reiniciar el tamaño de la secuencia
longitudSecuenciaActualLibres = 0
#Revisar cuando los días libres llegan hasta el final del horizonte de tiempo y no se descargó la secuencia
if longitudSecuenciaActualLibres !=0:
tamaniosSecuenciasLibres.append(longitudSecuenciaActualLibres)
#Salida de diagnóstico
print("---------------")
print(f"Estado de detección de secuencias libres {tamaniosSecuenciasLibres}")
print("---------------")
#Al finalizar todo el proceso, retornar la longitud mayor
if tamaniosSecuenciasLibres == list():
return 0#Asegurar el retorno si no se encuentran secuencias de días libres
else:
return max(tamaniosSecuenciasLibres)#Retornar la secuencia más larga encontrada
#Función para obtener el número de días consecutivos ocupados en la programación de un empleado
def numeroDiasTrabajandoConsecutivos(programacionEnRevision, horizonteTiempo):
#Estructura para acumular el tamaño de las secuencias con días ocupados consecutivos
tamaniosSecuenciasOcupados = []
#Inicializar la longitud de la secuencia actual
longitudSecuenciaActual = 0
#Recorrer todo el horizonte de tiempo
for i in range(horizonteTiempo):
#Si el día actual del empleado está ocupado
if programacionEnRevision['itinerario'][i] != dict():
longitudSecuenciaActual += 1
else:#El día está libre, se rompe la secuencia
#Acumular el tamaño de secuencia (si es diferente de cero)
if longitudSecuenciaActual != 0:
tamaniosSecuenciasOcupados.append(longitudSecuenciaActual)
#Reiniciar el tamaño de la secuencia
longitudSecuenciaActual = 0
#Revisar cuando los días libres llegan hasta el final del horizonte de tiempo y no se descargó la secuencia
if longitudSecuenciaActual !=0:
tamaniosSecuenciasOcupados.append(longitudSecuenciaActual)
#Salida de diagnóstico
print("---------------")
print(f"Estado de detección de secuencias ocupadas (bloques de trabajo encadenados) {tamaniosSecuenciasOcupados}")
print("---------------")
#Al finalizar todo el proceso, retornar la longitud mayor
if tamaniosSecuenciasOcupados == list():
return 0#Asegurar el retorno si no se encuentran secuencias de días trabajados
else:
return max(tamaniosSecuenciasOcupados)#Retornar la secuencia más larga encontrada
#Función para incorporar turno en el itinerario de un empleado (actualización de indicadores)
#--------------------------------------------------------------------------------------------
def incorporarTurno(programacionEnActualizacion, turnoEntrante):
# #Salida de diagnóstico
# print("Valores recibidos:")
# print("ProgramacionEnActualizacion")
# pp.pprint(programacionEnActualizacion)
# print("TurnoEntrante")
# pp.pprint(turnoEntrante)
#programacionEnActualizacion = programacionEnActualizacion.copy()
#Revisión de disponibilidad antes de incorporar y validar
if programacionEnActualizacion['itinerario'][ turnoEntrante['indiceDia'] ] == dict():
print("-->->->->->->- Antes de actualización -->->->->->->-")
pp.pprint(programacionEnActualizacion)
print("-->->->->->->- Antes de actualización -->->->->->->-")
#Actualización por referencia del itinerario
programacionEnActualizacion['itinerario'][ turnoEntrante['indiceDia'] ] = turnoEntrante
#Actualizar los demás valores
programacionEnActualizacion['totalDiasLibre'] -= 1
programacionEnActualizacion['diasTrabajo'] += 1
programacionEnActualizacion['conteoTiposTurno'][turnoEntrante['tipoTurno']] += 1
# #Actualizarlos de forma calculada
# programacionEnActualizacion['totalDiasLibre'] -= 1
# programacionEnActualizacion['diasTrabajo'] += 1
# programacionEnActualizacion['conteoTiposTurno'][turnoEntrante['tipoTurno']] += 1
#Actualización del número de días libre consecutivos
programacionEnActualizacion['diasLibreConsecutivos'] = numeroDiasLibresConsecutivos(programacionEnActualizacion, len(programacionEnActualizacion['itinerario']))
#Actualización del número de días trabajando consecutivos
programacionEnActualizacion['diasTrabajandoConsecutivos'] = numeroDiasTrabajandoConsecutivos(programacionEnActualizacion, len(programacionEnActualizacion['itinerario']) )
# #Mostrar actualización dentro de la función (diagnóstico)
# print("------------------")
# print("Estado del itinerario dentro d ela función")
# pp.pprint(programacionEnActualizacion)
# print("------------------")
print("-->->->->->->- Después de actualización -->->->->->->-")
pp.pprint(programacionEnActualizacion)
print("-->->->->->->- Después de actualización -->->->->->->-")
#input()#Pausa para revisar
else:#Si no había disponibilidad, retornar una bandera de -1 y realizar el control correspondiente
return -1
#Si se incorporó bajo disponibilidad, retornar programación para las validaciones restantesa
return programacionEnActualizacion.copy()
#Función para revisar cumplimiento de secuencias y condiciones en una programación de un empleado
def cumplimientoCondiciones(programacionEnRevision, instancia, tiposTurno):
#Revisar cumplimiento de número de días libres consecutivos en el itinerario (tanto límite inferior como límite superior)
cNumeroDiasLibresConsecutivos = programacionEnRevision['diasLibreConsecutivos'] <= instancia['longitudDiasLibre']['longitudMaxima']
#Revisar cumplimiento de límites de ocupación
cLimitesOcupacion = programacionEnRevision['diasTrabajandoConsecutivos'] <= instancia['longitudBloquesTrabajo']['longitudMaxima']
#Revisar si cumple el número de días mínimo y máximo de cada tipo de turno
booleanosTiposTurno = []
for tipoTurno in tiposTurno:
booleanosTiposTurno.append( programacionEnRevision['conteoTiposTurno'][tipoTurno] <= instancia['detalleTurnos'][tipoTurno]['longMaxBloques'] )
cTiposTurno = all(booleanosTiposTurno)
#Revisar que no hay secuencias prohibidas en la programación
#-----------------------------------------------------------
#Suponer que se cumplen las restricciones y falsear durante la revisión si es el casos
cSecuenciasNP_Longitud2 = True
cSecuenciasNP_Longitud3 = True
#NP Longitud 2
if instancia['numeroSecuenciasNoPermitidas']['SecuenciaDeLongitud2'] != 0:
#Recorrer la programación de 2 en 2 (en este caso 2 días)
for i in range(instancia['longitudProgramacion'] - 1):
#Generar versión string de la subsecuencia
strSubsecuencia = str()
#Día i-ésimo
if(programacionEnRevision['itinerario'][i]!=dict()):
strSubsecuencia += programacionEnRevision['itinerario'][i]['tipoTurno']
#Día i-ésimo + 1
if(programacionEnRevision['itinerario'][i+1]!=dict()):
strSubsecuencia += programacionEnRevision['itinerario'][i+1]['tipoTurno']
#Revisar si está en las prohibidas de longitud 2
if strSubsecuencia != str() and strSubsecuencia in instancia['strSecuenciasTurnosNP']['longitud2']:
cSecuenciasNP_Longitud2 = False #Falsear cuando incumple
#Salida de diagnóstico
print(f"Secuencias Prohibidas Longitud 2: {instancia['strSecuenciasTurnosNP']['longitud2']}")
print(f"Secuencia Incumpliendo: {strSubsecuencia}")
break #Detenerse para bajar costo computacional
#NP Longitud 3
if instancia['numeroSecuenciasNoPermitidas']['SecuenciaDeLongitud3'] != 0:
#Recorrer la programación de 3 en 3 (en este caso 3 días)
for i in range(instancia['longitudProgramacion'] - 2):
#Generar versión string de la subsecuencia
strSubsecuencia = str()
#Día i-ésimo
if(programacionEnRevision['itinerario'][i]!=dict()):
strSubsecuencia += programacionEnRevision['itinerario'][i]['tipoTurno']
else:
strSubsecuencia += '-'
#Día i-ésimo + 1
if(programacionEnRevision['itinerario'][i+1]!=dict()):
strSubsecuencia += programacionEnRevision['itinerario'][i+1]['tipoTurno']
else:
strSubsecuencia += '-'
#Día i-ésimo + 2
if(programacionEnRevision['itinerario'][i+2]!=dict()):
strSubsecuencia += programacionEnRevision['itinerario'][i+2]['tipoTurno']
else:
strSubsecuencia += '-'
#Revisar si está en las prohibidas de longitud 3
if strSubsecuencia != str() and strSubsecuencia in instancia['strSecuenciasTurnosNP']['longitud3']:
cSecuenciasNP_Longitud3 = False #Falsear cuando incumple
#Salida de diagnóstico
print(f"Secuencias Prohibidas Longitud 3: {instancia['strSecuenciasTurnosNP']['longitud3']}")
print(f"Secuencia Incumpliendo: {strSubsecuencia}")
break #Detenerse para bajar costo computacional
#Si cumple todas las condiciones, retornar verdadero para que sea incorporado
#Nota: en este trabajo se revisarán solamente límites superiores como experimento para observar si disminuye la ocupación de los tripulantes o empleados
if all([cNumeroDiasLibresConsecutivos,
cLimitesOcupacion,
cTiposTurno,
cSecuenciasNP_Longitud2,
cSecuenciasNP_Longitud3]):#Actualizar una vez estén implementadas todas las condiciones
#Reportar viabilidad al cumplir todas las condiciones
return True
else:
#Reportar inviabilidad al incumplir alguna de las condiciones para incorporación
return False